【问题标题】:request for member `...' is ambiguous in g++对成员“...”的请求在 g++ 中不明确
【发布时间】:2010-11-21 17:37:09
【问题描述】:

使用 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&lt;T&gt; 是一个模板接口,它只是表明该对象可以是某个匹配类型IListener&lt;T&gt; 的对象的告密者。所以让我恼火的是这样一个想法,即由于某种原因,函数是模棱两可的,据我所知,它们不是。 addListener() 方法针对不同的输入类型IListener&lt;const SConsolePacket&amp;&gt;IListener&lt;const SControlPacket&amp;&gt; 进行了重载。用法是:

m_controller->addListener( m_model );

其中m_model 是指向IRigidBody 对象的指针,而IRigidBody 仅继承自IListener&lt; const SControlPacket&amp; &gt;,并且绝对不是来自IListener&lt; const SConsolePacket&amp; &gt;

作为健全性检查,我使用 doxygen 生成类层次结构图,doxygen 同意我的观点,IRigidBody 不是从 IListener&lt; const SConsolePacket&amp; &gt; 派生的

显然我对 C++ 中继承的理解并不完全正确。我的印象是IListener&lt;const SControlPacket&amp;&gt;IListener&lt;const SConsolePacket&amp;&gt; 是两种不同的类型,函数声明

addListener(IListener<const SConsolePacket&>* listener)

addListener(IListener<const SControlPacket&>* listener)

声明两个独立的函数,它们根据输入参数的(不同的)不同类型做两件不同的事情。此外,我的印象是指向IRigidBody 的指针也是指向IListener&lt;const SControlPacket&amp;&gt; 的指针,并且通过调用addListener( m_model ),编译器应该明白我正在调用上述两个函数中的第二个。

我什至尝试像这样投射m_model

m_controller->addListener(
        static_cast<IListener<const SControlPacket&>*>(m_model) );

但仍然出现该错误。我一生都看不到这些功能是如何模棱两可的。谁能解释一下这个问题?

附:我知道如何通过这样做来强制函数明确:

m_controller->ISource<const SControlPacket&>::addListener( m_model );

我只是碰巧认为这非常不可读,我宁愿不必这样做。

编辑...开个玩笑。这显然不能解决问题,因为它会导致链接器错误:

CModelTesterGui.cpp:1312: undefined reference to `utility::ISource<aerobat::SControlPacket const&>::addListener(utility::IListener<SControlPacket const&>*)'

【问题讨论】:

  • SControlPacket和SConsolePacket是什么关系?
  • 你能补充一下为什么最后一行m_controller-&gt;ISource&lt;const SControlPacket&amp;&gt;::addListener( m_model ); 消除了通话的歧义吗?如果函数被重载,它们应该在同一个类中。这些函数是在哪里声明的?
  • @GRB 没有关系。两者都是从无到有的结构。 @litb 我错了。它使它可以编译,但是当链接器尝试查找纯虚拟的 ISource<...>::addListener(...) 时,结果会导致链接错误。现在我很困惑。当您说声明时,我假设您的意思是已定义。它们在派生自 IController 的具体类中定义。
  • @cheshirekow,我的意思是已声明,未定义。执行“a.B::f”会禁止虚拟调用机制。然后,如果您这样调用它,则必须存在纯虚函数的定义。对于名称查找,只有声明很重要。在这种情况下,使用声明可以阻止这些歧义。
  • @litb,知道了。你很准。也感谢您的回答!

标签: c++ gcc g++ ambiguous


【解决方案1】:

看来你的情况是这样的:

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;
};

现在,这两个名称在同一个范围内,现在它们可以互相重载。查找现在将停止在控制器类,而不是深入到两个基类分支。

【讨论】:

  • 不错!所以这就是这些东西的用途。我认为我从来没有真正理解过使用声明(没有真正理解名称查找的结果)。非常感谢。作为说明,我实际上将 using 声明放在 IController 接口中。我认为这是合适的地方。
  • 至少在g++中,不允许两次使用。您可以在派生类 C 中选择 A::f 或 B::f。
  • @Dingle prolly 一个旧版本的 g++ - fwiw(对于最近的用户),4.7.2 可以很好地处理两个使用
  • 好吧,稍微澄清一下,多变量继承的情况下我该怎么办?
  • 那么为什么标准要求编译器放弃它而不是在原始情况下进行重载解析?
猜你喜欢
  • 2014-07-10
  • 2021-01-25
  • 2020-10-14
  • 1970-01-01
  • 2023-02-22
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
相关资源
最近更新 更多