将指针形式的派生 class 方法转换为基础 class
Casting pointer form derived class method to base class
假设我想创建一个层次结构来响应以字符串编码的特定事件。例如来自网络的命令。想法是有一个 Base
class 处理网络连接、接收缓冲区、拆分它等和处理命令反应是在派生 class 中(派生 class 也可以添加一些需要处理的新词)。所以我的解决方案是:
class Base {
public:
typedef void (Base::*Method)();
typedef std::unordered_map<std::string, Method> Methods;
void reactToA();
void reactToB();
Base() :
methods{
{ "A", &Base::reactToA },
{ "B", &Base::reactToB }
}
{
}
void action( const std::string &s )
{
auto f = methods.find( s );
if( f != methods.end() )
(*this.*)(f->second)();
}
protected:
Methods methods;
};
class Child : public Base {
public:
void reactToB();
void reactToC();
Child() {
methods[ "B" ] = static_cast<Method>( &Child::reactToB );
methods[ "C" ] = static_cast<Method>( &Child::reactToC );
}
};
所以我必须将指向 Child
方法的指针转换为指向 Base
方法的指针。演员表定义明确吗?是否有更优雅(或正确,如果这导致 UB)解决方案?
来自[expr.static.cast]:
A prvalue of type “pointer to member of D
of type cv1 T
” can be converted to a prvalue of type “pointer to
member of B
” of type cv2 T
, where B
is a base class (Clause 10) of D
, if a valid standard conversion from
“pointer to member of B
of type T
” to “pointer to member of D
of type T
” exists (4.11), and cv2 is the same
cv-qualification as, or greater cv-qualification than, cv1. [...] If class B
contains the original member, or is a
base or derived class of the class containing the original member, the resulting pointer to member points to
the original member. Otherwise, the behavior is undefined.
在我们的例子中,&Base::reactToB
可以转换为 &Child::reactToB
,但由于 Base
不 包含原始成员,行为未定义。
您必须存储类似 std::function<void(Base*)>
或 void(*)(Base*)
的内容。
如果是前者,您可以向 Base
添加一个成员函数,例如:
template <typename C>
void addMethod(std::string const& name, void (C::*method)()) {
methods[name] = [method](Base* b){
(static_cast<C*>(b)->*method)();
};
}
addMethod("B", &Child::reactToB);
如果是后者,你可以这样做:
methods[ "B" ] = +[](Base* b){
static_cast<Child*>(b)->reactToB();
};
只需一点点通用函数指针的开销 std::function
,您就可以拥有完全定义的行为和更大的灵活性,因为您几乎可以调用任何东西,而不仅仅是方法:
class Base {
public:
typedef std::function<void()> Callable;
typedef std::unordered_map<std::string, Callable> Callables;
void action(const std::string &s) {
auto f = callables.find(s);
if (f != callables.end()) f->second();
}
protected:
Callables callables;
};
class Derived1 : public Base {
void reactToA() {}
void reactToB() {}
public:
Derived1() {
callables["A"] = std::bind(&Derived1::reactToA, *this);
callables["B"] = std::bind(&Derived1::reactToB, *this);
}
};
static void reactToE();
class Derived2 : public Derived {
void reactToB() {}
void reactToC() {}
public:
Derived2() {
callables["B"] = std::bind(&Derived2::reactToB, *this);
callables["C"] = std::bind(&Derived2::reactToC, *this);
callables["D"] = []{ std::cout << "Hey, what, D now?!" << std::endl; }
callables["E"] = &reactToE;
}
};
假设我想创建一个层次结构来响应以字符串编码的特定事件。例如来自网络的命令。想法是有一个 Base
class 处理网络连接、接收缓冲区、拆分它等和处理命令反应是在派生 class 中(派生 class 也可以添加一些需要处理的新词)。所以我的解决方案是:
class Base {
public:
typedef void (Base::*Method)();
typedef std::unordered_map<std::string, Method> Methods;
void reactToA();
void reactToB();
Base() :
methods{
{ "A", &Base::reactToA },
{ "B", &Base::reactToB }
}
{
}
void action( const std::string &s )
{
auto f = methods.find( s );
if( f != methods.end() )
(*this.*)(f->second)();
}
protected:
Methods methods;
};
class Child : public Base {
public:
void reactToB();
void reactToC();
Child() {
methods[ "B" ] = static_cast<Method>( &Child::reactToB );
methods[ "C" ] = static_cast<Method>( &Child::reactToC );
}
};
所以我必须将指向 Child
方法的指针转换为指向 Base
方法的指针。演员表定义明确吗?是否有更优雅(或正确,如果这导致 UB)解决方案?
来自[expr.static.cast]:
A prvalue of type “pointer to member of
D
of type cv1T
” can be converted to a prvalue of type “pointer to member ofB
” of type cv2T
, whereB
is a base class (Clause 10) ofD
, if a valid standard conversion from “pointer to member ofB
of typeT
” to “pointer to member ofD
of typeT
” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If classB
contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined.
在我们的例子中,&Base::reactToB
可以转换为 &Child::reactToB
,但由于 Base
不 包含原始成员,行为未定义。
您必须存储类似 std::function<void(Base*)>
或 void(*)(Base*)
的内容。
如果是前者,您可以向 Base
添加一个成员函数,例如:
template <typename C>
void addMethod(std::string const& name, void (C::*method)()) {
methods[name] = [method](Base* b){
(static_cast<C*>(b)->*method)();
};
}
addMethod("B", &Child::reactToB);
如果是后者,你可以这样做:
methods[ "B" ] = +[](Base* b){
static_cast<Child*>(b)->reactToB();
};
只需一点点通用函数指针的开销 std::function
,您就可以拥有完全定义的行为和更大的灵活性,因为您几乎可以调用任何东西,而不仅仅是方法:
class Base {
public:
typedef std::function<void()> Callable;
typedef std::unordered_map<std::string, Callable> Callables;
void action(const std::string &s) {
auto f = callables.find(s);
if (f != callables.end()) f->second();
}
protected:
Callables callables;
};
class Derived1 : public Base {
void reactToA() {}
void reactToB() {}
public:
Derived1() {
callables["A"] = std::bind(&Derived1::reactToA, *this);
callables["B"] = std::bind(&Derived1::reactToB, *this);
}
};
static void reactToE();
class Derived2 : public Derived {
void reactToB() {}
void reactToC() {}
public:
Derived2() {
callables["B"] = std::bind(&Derived2::reactToB, *this);
callables["C"] = std::bind(&Derived2::reactToC, *this);
callables["D"] = []{ std::cout << "Hey, what, D now?!" << std::endl; }
callables["E"] = &reactToE;
}
};