将指针从派生类方法转换为基类

Casting pointer form derived class method to base class

本文关键字:转换 基类 类方法 派生 指针      更新时间:2023-10-16

假设我想创建一个层次结构,该层次结构对以字符串编码的特定事件做出反应。例如来自网络的命令。设想有一个Base类,处理网络连接、接收缓冲区、分割缓冲区等,处理命令反应,在派生类中(派生类也可以添加一些要处理的新单词)。所以我的解决方案是:

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方法的指针。这个cast定义好了吗?是否有更优雅(或正确,如果这导致UB)的解决方案?

From [expr.static.cast]:

类型为"指向cv1 T类型的D成员的指针"的右值可以转换为类型为"指向的指针"的右值类型cv2 TB ' '成员,其中BD的基类(第10条),如果从存在"指向BT类型成员的指针"到"指向DT类型成员的指针"(4.11),cv2也一样cv-qualification等于或大于cv-qualification, cv1。[…如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,结果指向成员的指针指向原始成员。

在本例中,&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;
  }
};