对成员“..."的请求在 g++ 中不明确

2021-12-18 00:00:00 gcc g++ c++ ambiguous

我在我的一个类中使用 gcc 3.4.5 (mingw) 遇到以下编译错误:

I'm getting the following compile error in one of my classes, using 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 是一个模板接口,它只是表明对象可以是某种匹配类型的对象的通知者 IListener.所以让我恼火的是,由于某种原因,函数是模棱两可的,而据我所知,它们不是.addListener() 方法针对不同的输入类型IListenerIListener 重载.用法是:

Hopefully you can see that ISource<T> is a template interface that just indicates that the object can be an informer for an object that is of some matching type IListener<T>. So the thing that has me irked is this idea that for some reason functions are ambiguous when, as far as I can tell, they are not. The addListener() method is overloaded for different input types IListener<const SConsolePacket&> and IListener<const SControlPacket&>. The usage is:

m_controller->addListener( m_model );

其中m_model 是一个指向IRigidBody 对象的指针,并且IRigidBody 仅继承自IListener<;const SControlPacket&> 并且绝对不是来自 IListener

Where m_model is a pointer to an IRigidBody object, and IRigidBody inherits only from IListener< const SControlPacket& > and definately not from IListener< const SConsolePacket& >

作为健全性检查,我使用 doxygen 生成类层次结构图,并且 doxygen 同意我的观点,即 IRigidBody 并非源自 IListener<;const SConsolePacket&>

As a sanity check, I used doxygen to generate the class hierarchy diagram and doxygen agrees with me that IRigidBody does not derive from IListener< const SConsolePacket& >

显然我对 C++ 继承的理解并不完全正确.我的印象是 IListenerIListener 是两种不同的类型,并且函数声明

Evidently my understanding of inheritence in c++ is not exactly correct. I'm under the impression that IListener<const SControlPacket&> and IListener<const SConsolePacket&> are two different types, and that the function declarations

addListener(IListener<const SConsolePacket&>* listener)

addListener(IListener<const SControlPacket&>* listener)

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

declare two separate functions that do two separate things depending on the (distinct) different type of the parameter that is input. Furthermore, I'm under the impression that a pointer to an IRigidBody is also a pointer to an IListener<const SControlPacket&> and that by calling addListener( m_model ) the compiler should understand that I'm calling the second of the above two functions.

我什至尝试像这样转换 m_model:

I even tried casting m_model like this:

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

但仍然得到那个错误.我一生都看不到这些功能是如何模棱两可的.任何人都可以阐明这个问题吗?

but still get that error. I cannot for the life of me see how these functions are ambiguous. Can anyone shed light on this issue?

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

P.S. I know how to force the function to be un-ambiguous by doing this:

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

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

I just happen to think that is terribly unreadible and I would prefer not to have to do that.

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

Edit... just kidding. That apparently doesn't fix the problem as it leads to a linker error:

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

推荐答案

看起来你的情况是这样的:

Looks like your situation is like this:

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 范围内查找名称,并且不会进一步查找:

The second call to f is ambiguous, because in looking up the name, it finds functions in two different base class scopes. In this situation, the lookup is ambiguous - they don't overload each other. A fix would be to use a using declaration for each member name. Lookup will find names in the scope of C and don't lookup further:

struct C : A, B { using A::f; using B::f; };

现在,该调用将找到两个函数,进行重载解析,并发现使用 int 的那个将适合.转到您的代码,这意味着您必须执行以下操作

Now, the call would find two functions, do overload resolution, and find that the one taking int will fit. Carried over to your code, it would mean that you have to do something like the following

struct controller : ISource<const SConsolePacket&>, ISource<const SControlPacket&> {
  using ISource<const SConsolePacket&>::addListener;
  using ISource<const SControlPacket&>::addListener;
};

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

Now, the two names are in the same scope, and now they can overload each other. Lookup will now stop at the controller class, not diving further into the two base-class branches.

相关文章