指向成员函数的指针和多重继承
Pointer-to-member-function and multiple inheritance
我无法控制的 A class Base
有一个函数接受指向任何 class 函数的成员指针。它的用途如下:
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); // (*)
}
};
认为多重继承也不错,只要我们只用它来继承一个接口,而且只有一个"concrete"基class.
模板化是因为有时我们想从 Base
派生,有时我们想从它派生的 classes 之一派生(我也无权访问)- 如果那与你无关可以假装我写了 Base
而不是 T
并删除模板。
无论如何,我现在遇到的问题是,当我打电话给
LifeAndUniverseCalculator calc;
calc.bindProperties();
int answer = calc.get("answer");
我胡言乱语。我想它可能是指向 vtables 的指针,所以我尝试替换
T::bindProperty("answer", &ICalculator::getAnswer);
来自
T::bindProperty("answer", &LifeAndUniverseCalculator::getAnswer);
希望它能正确计算偏移量,但显然这行不通(正如你现在已经知道的,我真的在猜测这一切是如何工作的)。
我想到了一些方案,比如
摆脱多重继承,把所有东西都放在ICalculator
里,直接放在LifeAndUniverseCalculator
里(这是唯一的派生class)
为 LifeAndUniverseCalculator
中的所有 ICalculator
内容创建包装函数,例如LifeAndUniverseCalculator::Calculator_GetAnswer
只是调用 ICalculator::GetAnswer
.
我想知道
- 最好能有什么简单的方法修复标有(*)的那一行?
- 如果不是,最佳解决方案是什么(上述备选方案之一,或其他方案)?
- 如果我能够联系 class
Base
的作者并且他们愿意并且能够更改他们的 class,我具体需要问什么,如果你能根据我的描述说点有道理的。
如果您需要 MCVE,我认为有一个解决了问题 on IDEOne。
在您的 MCVE 中,函数 A::bindFunction
(类似于简化代码中的 Base::bindProperty
)强制将 B
的函数成员强制转换为 [=15= 的成员函数].这让我觉得这是根本问题。这可以通过将 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
但是,我知道我更改的代码属于您明确提到的不能修改的 class。这确实是 Base
所做的——它将其他 class 的成员函数转换为它自己的成员函数吗? (或许,虽然我的修复使代码正常工作,但我错误地识别了问题)。
A class Base
有一个函数接受指向任何 class 函数的成员指针。它的用途如下:
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); // (*)
}
};
认为多重继承也不错,只要我们只用它来继承一个接口,而且只有一个"concrete"基class.
模板化是因为有时我们想从 Base
派生,有时我们想从它派生的 classes 之一派生(我也无权访问)- 如果那与你无关可以假装我写了 Base
而不是 T
并删除模板。
无论如何,我现在遇到的问题是,当我打电话给
LifeAndUniverseCalculator calc;
calc.bindProperties();
int answer = calc.get("answer");
我胡言乱语。我想它可能是指向 vtables 的指针,所以我尝试替换
T::bindProperty("answer", &ICalculator::getAnswer);
来自
T::bindProperty("answer", &LifeAndUniverseCalculator::getAnswer);
希望它能正确计算偏移量,但显然这行不通(正如你现在已经知道的,我真的在猜测这一切是如何工作的)。
我想到了一些方案,比如
摆脱多重继承,把所有东西都放在
ICalculator
里,直接放在LifeAndUniverseCalculator
里(这是唯一的派生class)为
LifeAndUniverseCalculator
中的所有ICalculator
内容创建包装函数,例如LifeAndUniverseCalculator::Calculator_GetAnswer
只是调用ICalculator::GetAnswer
.
我想知道
- 最好能有什么简单的方法修复标有(*)的那一行?
- 如果不是,最佳解决方案是什么(上述备选方案之一,或其他方案)?
- 如果我能够联系 class
Base
的作者并且他们愿意并且能够更改他们的 class,我具体需要问什么,如果你能根据我的描述说点有道理的。
如果您需要 MCVE,我认为有一个解决了问题 on IDEOne。
在您的 MCVE 中,函数 A::bindFunction
(类似于简化代码中的 Base::bindProperty
)强制将 B
的函数成员强制转换为 [=15= 的成员函数].这让我觉得这是根本问题。这可以通过将 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
但是,我知道我更改的代码属于您明确提到的不能修改的 class。这确实是 Base
所做的——它将其他 class 的成员函数转换为它自己的成员函数吗? (或许,虽然我的修复使代码正常工作,但我错误地识别了问题)。