9

我有一个真实的情况,可以在下面的例子来概括:暧昧多重继承

template< typename ListenerType > 
struct Notifier 
{ 
    void add_listener(ListenerType&){} 
}; 

struct TimeListener{ }; 
struct SpaceListener{ }; 

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 

}; 

struct B : TimeListener{ }; 

int main() 
{ 
    A a; 
    B b; 

    a.add_listener(b); // why is ambiguous? 

    return 0; 
} 

为什么不明显的编译器BTimeListener,因此唯一可能重载分辨率是Notifier<TimeListener>::add_listener(TimeListener&)

+4

您可以用'使用通知解决您的问题:: add_listener;'(另一个)在'struct A'中。 [Demo](http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

回答

8

成员名称查找规则说,你的代码是模糊的,因为名称在两个基类中找到,因此查找集合无效。您不需要熟悉查找集和合并的所有细节;重要的细节是检查两个基类,并在两个中找到名称add_listener,这会产生歧义。

简单的解决方法是使用using声明将这些基类名称带入A。这意味着add_listener两个版本中查找在A,而不是在基类,所以没有合并歧义:

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
    //plus any more base classes 
}; 

Live Demo

5

标准指示的编译器不够智能来解析符号 - 尽管在这种情况下可以逻辑地解决它,但它被定义为模糊操作。您的编译器可能只查找符号名称,而不是查找可能的符号后的原型。

你可以告诉编译器,你明确接受这两种类型,通过明确你知道应该被接受的模板符号。这将使编译器接受任一表单并应用模板。下面是一个例子。我不能在我的电脑目前测试这一点,但它应该工作,如果编译器有困难解决您的原来的例子符号:

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
}; 
+6

这并不是说编译器不够聪明,标准说这是不明确的。 – TartanLlama

+0

好点。我试图澄清我的意思是“不够聪明”来封装定义的歧义。 – Pyrce