我做了一个小例子,试图用我那可怜的英语向你解释我想做什么:)。
我有一个班机是我的引擎。这是我的几个孩子的父母班。
这是父类:
#include <string>
#include <iostream>
#include <vector>
template <typename Type>
class A
{
public:
A(std::string const &str)
: m_str(str)
{
}
void run(void) const {
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++) {
if(m_str == ACTIONS[i].key) {
return ((*(this).*ACTIONS[i].f)(m_str));
}
}
}
protected:
typedef struct s_action {
std::string key;
void (Type::*f)(std::string const &);
} t_action;
static t_action const ACTIONS[];
std::string m_str;
};
class B : public A<B>
{
public:
B(std::string const &str);
protected:
static t_action const ACTIONS[];
void error(std::string const &str);
void success(std::string const &str);
};
我想在该父类A :: run中使用成员函数的表指针调用子方法,如上所示
此代码无法编译。
我知道不可能有一个静态变量virtual,但是 正是我需要为A :: ACTIONS做的。我绝对需要将B :: ACTIONS初始化为A :: run作品。
首先有可能吗?您有这种情况的一个小例子吗?
这是我的小代码的结尾:
#include "Class.hpp"
B::t_action const B::ACTIONS[] = {
{"ERROR", &B::error},
{"SUCCESS", &B::success},
{"", nullptr}
};
B::B(std::string const &str)
: A<B>(str)
{
}
void B::error(std::string const &str) {
std::cerr << str << std::endl;
}
void B::success(std::string const &str) {
std::cout << str <<std::endl;
}
主要:
#include "Class.hpp"
int main() {
B b("SUCCESS");
b.run();
return (0);
}
我没有尝试,通常此代码应在标准输出上显示成功
谢谢您的帮助
答案 0 :(得分:2)
void run(void) const
{
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++)
if (m_str == ACTIONS[i].key)
return ((*(this).*ACTIONS[i].f)(m_str));
}
无法编译有多种原因。不是一个原因,但是有几个原因。整个调度机制必须完全重新设计。
第一件事就是这是
void run(void) const
一种const
类方法。
有问题的方法指针是:
void (Type::*f)(std::string const &);
方法指针不是const
,而是可变的。从现有的const
类方法中,您只能调用其他const
方法。您不能直接或间接地从const
类方法中调用非const
方法。
因此,首要任务是将其更改为
void (Type::*f)(std::string const &) const;
这也意味着子类error()
和success()
中的所有方法也必须也是const
类方法。
如果有必要将此分发机制与非const
方法一起使用,则run()
方法不能是const
类方法本身。但这不是唯一的问题,因此我将继续使用const
方法。
return ((*(this).*ACTIONS[i].f)(m_str));
这里的this
是A<Type>
。这是该类的方法。这就是this
在这里。
方法指针f
是指向Type
而不是A<Type>
的方法的指针。 Type
是A<Type>
的子类,您不能将指针或对基类的引用转换为指针或对子类的引用,而只能将指针转换为{{1 }},并在A
继承自B
时转换为指向B
的指针。 C ++无法以这种方式工作。
解决方案很简单,只需要进行一些小调整。这个A
应该引用一个run()
,并通过传入的引用来调用该方法,然后替换抽象const Type &
的方法来调用它,并将run()
作为参数传递:
*this
然后,每个继承此模板的子类仅需要实现一个简单的外观:
public:
virtual void run()=0;
protected:
void run_me(const Type &me) const
{
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++)
if (m_str == ACTIONS[i].key)
return ((me.*ACTIONS[i].f)(m_str));
}
编辑:这解决了编译错误,但是还需要其他工作来处理不能覆盖静态类成员的事实。该解决方案也非常简单:还可以利用虚拟类方法来实现此目的。
从模板基类中删除class B : public A<B>
{
public:
void run() const override
{
run_me(*this);
}
的声明,并将其替换为抽象函数:
ACTIONS
并在virtual const t_action *get_actions() const=0;
中使用它:
run_me()
const t_action *ACTIONS=this->get_actions();
的其余部分保持不变,然后在子类中实现run_me()
:
get_actions()
几乎所有其他内容都保持不变。
答案 1 :(得分:1)
问题在于A
将始终使用自己定义的一组操作,而不是B
的操作。
您根本不需要创建A
,因为您想使用B
方法和方法列表。
假设您首先创建一个run
调用函数:
template<typename T>
void run(T* obj, const std::string method)
{
const auto& available_methods = obj->get_methods();
auto iter = available_methods.find(method);
if(iter == available_methods.end())
{
// Handle this case
}
std::invoke(iter->second, obj); //C++17, or (obj->*(iter->second))();
}
现在,对于课程B
,您需要一些非常简单的内容:
class B
{
public:
typedef std::unordered_map<std::string, void(B::*)()> MethodMap;
void foo();
static MethodMap& get_methods()
{
static MethodMap map{{"foo", &B::foo}};
return map;
}
};
在静态函数中使用get_methods()
填充地图,然后通过以下方式调用运行:
int main()
{
B b;
run(&b, "foo");
}
答案 2 :(得分:0)
如果您要使用CRTP和IMO,则需要先通过Google搜索CRTP。 顺便说一下,这是一个快速直接回答2你的q:
template<typename crtp_child>
class crtp_base{
using crtp_target=crtp_child;
auto crtp_this(){
return static_cast<crtp_target*>(this);
};
auto crtp_this() const {
return static_cast<crtp_target const*>(this);
};
public:
void run(){
auto range=crtp_this()->actions.equal_range(m_str);
for(auto entry:range)
(crtp_this()->*(entry.second))(m_str);
};
protected:
crtp_base(std::string str):
m_str(str)
{};
std::string m_str;
//...
};
struct crtp_user:
crtp_base<crtp_user>
{
using crtp_base::crtp_base;//ctor fwding
protected:
friend class crtp_base<crtp_user>;
std::unordered_multimap<std::string, void (crtp_user::*)(std::string)> actions;
//...
};