我有一个真实情况,可以在以下示例中进行总结:
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;
}
编译器为什么B
是TimeListener
并不明显,因此唯一可能的重载解析是Notifier< TimeListener >::add_listener( TimeListener& )
?
答案 0 :(得分:8)
成员名称的查找规则表明您的代码不明确,因为该名称位于两个基类中,因此查找集无效。您不需要熟悉查找集和合并的所有细节;重要的细节是检查两个基类并在两者中找到名称add_listener
,这会产生歧义。
简单的解决方法是使用using-declarations将这些基类名称带入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
};
答案 1 :(得分:5)
标准指示编译器不够智能来解析符号 - 它被定义为一个模糊的操作,尽管你可以在这个实例中逻辑地处理它。您的编译器可能只是在找到两个可能的符号后才查找符号名称而不是原型。
您可以通过消除您知道应该接受的模板符号的歧义来告诉编译器您明确接受这两种类型。这将使编译器接受任一表单,然后应用模板。以下是此示例。我目前无法在我的计算机上测试它,但是如果编译器在原始示例中难以解析符号,它应该可以工作:
struct A : public Notifier< TimeListener >
, public Notifier< SpaceListener >
{
using Notifier< TimeListener >::add_listener;
using Notifier< SpaceListener >::add_listener;
};