成员`...'的请求在g ++中是不明确的

时间:2009-08-21 16:52:57

标签: c++ gcc g++ ambiguous

我在我的一个类中遇到以下编译错误,使用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&>*)'

1 个答案:

答案 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现在将停在控制器类,而不是进一步深入两个基类分支。