将指针形式的派生 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;
  }
};