我在我的一个类中遇到以下编译错误,使用gcc 3.4.5(mingw):
src/ModelTester/CModelTesterGui.cpp:1308: error: request for member `addListener' is ambiguous
include/utility/ISource.h:26: error: candidates are: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SConsolePacket&]
include/utility/ISource.h:26: error: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SControlPacket&]
希望您可以看到ISource<T>
是一个模板接口,它只是指示该对象可以是某个匹配类型为IListener<T>
的对象的线人。让我烦恼的是这个想法,由于某种原因,功能是模棱两可的,据我所知,它们不是。 addListener()
方法针对不同的输入类型IListener<const SConsolePacket&>
和IListener<const SControlPacket&>
进行了重载。用法是:
m_controller->addListener( m_model );
其中m_model
是指向IRigidBody
对象的指针,而IRigidBody
仅从IListener< const SControlPacket& >
继承,绝不是从IListener< const SConsolePacket& >
继承
作为一个完整性检查,我使用doxygen来生成类层次结构图,doxygen同意我IRigidBody
不是来自IListener< const SConsolePacket& >
显然,我对c ++中的继承性的理解并不完全正确。我的印象是IListener<const SControlPacket&>
和IListener<const SConsolePacket&>
是两种不同的类型,而且函数声明
addListener(IListener<const SConsolePacket&>* listener)
和
addListener(IListener<const SControlPacket&>* listener)
声明两个单独的函数,它们根据输入参数的(不同的)不同类型执行两个不同的操作。此外,我的印象是指向IRigidBody
的指针也是指向IListener<const SControlPacket&>
的指针,并且通过调用addListener( m_model )
,编译器应该理解我正在调用第二个以上两个功能。
我甚至试过像这样投射m_model
:
m_controller->addListener(
static_cast<IListener<const SControlPacket&>*>(m_model) );
但仍然会出现错误。我不能为我的生活看到这些功能是如何模棱两可的。任何人都可以解释这个问题吗?
P.S。我知道如何通过这样做强迫函数不明确:
m_controller->ISource<const SControlPacket&>::addListener( m_model );
我碰巧认为这是非常不可读的,我宁愿不必这样做。
编辑......开玩笑吧。这显然无法解决问题,因为它会导致链接器错误:
CModelTesterGui.cpp:1312: undefined reference to `utility::ISource<aerobat::SControlPacket const&>::addListener(utility::IListener<SControlPacket const&>*)'
答案 0 :(得分:24)
看起来你的情况是这样的:
struct A {
void f();
};
struct B {
void f(int);
};
struct C : A, B { };
int main() {
C c;
c.B::f(1); // not ambiguous
c.f(1); // ambiguous
}
对f的第二次调用是不明确的,因为在查找名称时,它会在两个不同的基类范围中查找函数。在这种情况下,查找是不明确的 - 它们不会相互过载。修复方法是为每个成员名称使用using声明。查找将在C
的范围内找到名称,不要进一步查找:
struct C : A, B { using A::f; using B::f; };
现在,调用将找到两个函数,执行重载解析,并发现采用int
的函数将适合。继承您的代码,这意味着您必须执行以下操作
struct controller : ISource<const SConsolePacket&>, ISource<const SControlPacket&> {
using ISource<const SConsolePacket&>::addListener;
using ISource<const SControlPacket&>::addListener;
};
现在,这两个名称在同一范围内,现在它们可以相互重载。 Lookup现在将停在控制器类,而不是进一步深入两个基类分支。