指针到成员函数和多重继承

Pointer-to-member-function and multiple inheritance

本文关键字:多重继承 函数 成员 指针      更新时间:2023-10-16

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

class Derived : public Base {
void bindProperties() {
Base::bindProperty("answerswer", &Derived::getAnswer);
}
int getAnswer() const { return 42; }
};

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

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

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("answerswer", &ICalculator::getAnswer);  // (*)
}
};

认为多重继承还不错,只要我们只用它来继承一个接口,只有一个"具体"的基类。

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

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

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

我胡言乱语。我认为这可能是带有指向 vtables 的指针的东西,所以我尝试替换

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

T::bindProperty("answerswer", &LifeAndUniverseCalculator::getAnswer);

希望它能正确计算偏移量,但显然这是行不通的(正如您现在已经发现的那样,我真的在猜测这一切是如何工作的)。

我想到了一些选择,例如

  • 摆脱多重继承并将所有内容ICalculator直接放在LifeAndUniverseCalculator中(这是唯一的派生类)

  • LifeAndUniverseCalculator中的所有ICalculator东西创建包装函数,例如LifeAndUniverseCalculator::Calculator_GetAnswer只是打电话给ICalculator::GetAnswer.

我想知道

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

如果您需要MCVE,我认为有一个可以解决IDEOne上的问题。

在 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所做的吗 - 它将其他类的成员函数强制转换为自身的成员函数?(或者,也许,虽然我的修复使代码正常工作,但我错误地识别了问题)。