指向成员函数和多重继承

时间:2017-05-16 16:12:51

标签: c++ multiple-inheritance pointer-to-member

我无法控制的类Base有一个函数接受指向任何类函数的成员指针。它的用法如下:

class Derived : public Base {
  void bindProperties() {
    Base::bindProperty("answer", &Derived::getAnswer);
  }

  int getAnswer() const { return 42; }
};

某种方式(我既不知道也不关心),Base存储此指针,后来允许我调用Derived::get("answer")(当然,这是一种简化的情况)。

缺点是,我们过去曾试图变得聪明,并使用了多重继承:

class ICalculator {
  virtual int getAnswer() const;
};

template<class T>
class LifeAndUniverseCalculator : public T, public ICalculator {
  virtual int getAnswer() const /* override */ { return 42; }

  void bindProperties() {
    T::bindProperty("answer", &ICalculator::getAnswer);  // (*)
  }
};

认为多重继承并不坏,只要我们只使用它继承一个接口并且只有一个“具体”基类。

模板是因为有时候我们想要从Base派生,有时候从它的一个派生类派生(我也无法访问) - 如果这是无关紧要的,你可以假装我写{{1而不是Base并删除模板。

无论如何,我现在遇到的问题是,当我打电话时

T

我得到了胡言乱语。我认为它可能是指向vtable的指针,所以我尝试替换

LifeAndUniverseCalculator calc;
calc.bindProperties();
int answer = calc.get("answer");

通过

    T::bindProperty("answer", &ICalculator::getAnswer);

希望能正确计算偏移量,但显然不起作用(正如你现在已经想到的那样,我真的第二次猜测这一切是如何起作用的。)

我想到了一些选项,比如

  • 摆脱多重继承并将 T::bindProperty("answer", &LifeAndUniverseCalculator::getAnswer); 中的所有内容直接放入ICalculator(这是唯一的派生类)

  • LifeAndUniverseCalculator中的所有ICalculator内容创建包装函数,例如LifeAndUniverseCalculator只需拨打LifeAndUniverseCalculator::Calculator_GetAnswer

我想知道

  • 最好是否有办法以简单的方式修复标有(*)的行?
  • 如果没有,最佳解决方案是什么(上述替代方案之一,或其他方式)?
  • 如果我能够联系班级ICalculator::GetAnswer的作者并且他们愿意并且能够改变他们的班级,那么具体我需要问什么,如果你能根据我的描述说些合理的话

如果您需要MCVE,我认为有一个可以捕获问题on IDEOne

1 个答案:

答案 0 :(得分:1)

在你的MCVE中,函数A::bindFunction(类似于简化代码中的Base::bindProperty)强制将B函数的成员强制转换为A的成员函数。这是根本问题。可以通过将A::f的类型更改为std::function<int(void)>

来解决此问题
class A
  : public ABase {
public:
    // int a, b;

    class Unknown{};
    typedef int(A::*Function)();

    template<typename T, typename Func>
    void bindFunction(T* owner, Func myf) { 
        f = std::bind(myf,owner);
    }

    int call() {
        return f();
    }

    //Function f;
    std::function<int(void)> f;
};

...

class Combined
 : public A, public B {
public:     
    Combined(int value) : B(value), A() {}

    virtual void /*A::*/bind() /* override */ {
        A::bindFunction( this, &Combined::getValue );
    }
};

只有这个改变,你的MCVE才能工作,打印出来

The answer to Life, The Universe and Everything is 42

但是,我知道我更改的代码属于您明确提到的无法修改的类。这确实是Base的作用 - 它将其他类的成员函数强制转换为其自身的成员函数吗? (或许,当我的修复程序使代码工作时,我错误地识别了问题)。