struct A {
void f(int x) {}
};
struct B {
template<typename T> void f(T x) {}
};
struct C : public A, public B {};
struct D {
void f(int x){}
template<typename T> void f(T x) {}
};
int main(int argc, char **argv) {
C c;
c.f<int>(3);
D d;
d.f<int>(3);
}
调用d.f
的原因是什么,c.f
给出了
error: request for member ‘f’ is ambiguous
error: candidates are: template<class T> void B::f(T)
error: void A::f(int)
答案 0 :(得分:11)
第一部分是由于成员名称查找,这就是它失败的原因。
我会推荐你:10.2/2 Member name lookup
以下步骤定义类范围中的名称查找结果, C.首先,每个类中的名称和每个声明 考虑其基类子对象。成员名称f合二为一 如果A是基础,则子对象B在子对象A中隐藏成员名称f B的类子对象。任何隐藏的声明都是 从考虑中消除。这些声明中的每一个都是 由使用声明引入的被认为是来自每个 包含声明的类型的C的子对象 由使用声明指定。
如果生成的声明集不是全部来自子对象 相同类型,或集合具有非静态成员并包括成员 来自不同的子对象,有一个歧义,程序是 病态的即可。否则该集合是查找的结果。
现在,关于模板功能的问题。
根据13.3.1/7 Candidate functions and argument list
在候选人是职能模板的每种情况下,候选人 使用模板生成函数模板特化 论证推论(14.8.3,14.8.2)。那些候选人然后被处理 候选人以通常的方式运作。给定的名称可以指一个 或更多功能模板以及一组重载 非模板功能。在这种情况下,候选功能 从每个函数模板生成的结果与组合 非模板候选函数。
如果你继续阅读13.3.3/1 Best viable function
在以下情况下,F1被视为更好的功能:
F1是非模板函数,F2是函数模板 专业化
这就是为什么以下代码段编译并运行非模板函数而没有错误:
D c;
c.f(1);
答案 1 :(得分:1)
我认为编译器无缘无故地优先于A::f
(非模板函数)B::f
。
这似乎是编译器实现错误,而不是依赖于实现的细节。
如果您添加以下行,则会选择compilation goes fine并选择正确的函数B::f<>
:
struct C : public A, public B {
using A::f; // optional
using B::f;
};
[有趣的是,::f
未被纳入C
的范围之前,它们被视为外来函数。]
答案 2 :(得分:0)
编译器不知道从C类调用哪个方法,因为在int类型的情况下,模板化方法将在void f(int)中进行转换,因此您有两个具有相同名称和相同参数但不同成员的方法家长班。
template<typename T> void f(T x) {}
或
void f(int)
试试这个:
c.B::f<int>(3);
或A类的这个:
c.A::f(3);
答案 3 :(得分:0)
考虑这个更简单的例子:
struct A{
void f(int x){}
};
struct B{
void f(float t){}
};
struct C:public A,public B{
};
struct D{
void f(float n){}
void f(int n){}
};
int main(){
C c;
c.f(3);
D d;
d.f(3);
}
在此示例中,与您的相同,D
编译但C
不编译。
如果类是派生类,则成员查找机制的行为不同。它检查每个基类并合并它们:在C
的情况下;每个基类都匹配查找(A :: f(int)和B :: f(float))。合并后C
决定它们是模棱两可的。
对于案例类D
:选择int
版本而不是float
,因为参数是整数。
答案 4 :(得分:0)
可能发生的情况是模板实例化是针对类A
和B
单独进行的,因此以两个void f(int)
函数结束。
这不会发生在D
中,因为编译器知道void f(int)
函数是专门化的,因此不会T
int
专门化。{/ p>