将指针形式转换为基类的派生类方法

时间:2015-08-14 15:45:31

标签: c++ pointer-to-member

让我们说我想创建一个层次结构来响应以字符串编码的某个事件。例如来自网络的命令。想法是有一个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方法的指针。这个演员定义得很好吗?是否更优雅(或更正确,如果这导致UB)解决方案?

2 个答案:

答案 0 :(得分:4)

来自[expr.static.cast]:

  

“指向 cv1 D类型的T成员的类型的prvalue”可以转换为类型为“指针”的prvalue    cv2 B类型的T“成员,其中BD的基类(第10条),如果是有效的标准转换从   “指向B类型T成员的指针”指向“指向D T类型成员的指针”(4.11), cv2 是一样的    cv - 限定为 cv - 资格, 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();
};

答案 1 :(得分:2)

只需要通用函数指针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;
  }
};