27
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)
4

5 回答 5

11

第一部分是由于成员名称查找,这就是它失败的原因。

我建议您参考:10.2/2 Member name lookup

以下步骤定义在类范围 C 中名称查找的结果。首先,考虑类中及其每个基类子对象中名称的每个声明。如果 A 是 B 的基类子对象,则一个子对象 B 中的成员名称 f 隐藏子对象 A 中的成员名称 f。如此隐藏的任何声明都将不予考虑。由 using 声明引入的这些声明中的每一个都被认为来自 C 的每个子对象,这些子对象的类型包含由 using 声明指定的声明。

如果声明的结果集并非全部来自相同类型的子对象,或者该集具有非静态成员并包括来自不同子对象的成员,则存在歧义并且程序格式错误。否则,该集合是查找的结果。

现在,关于模板函数的问题。

按照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);
于 2012-04-02T11:19:40.590 回答
1

我相信编译器无缘无故更喜欢A::f(非模板函数) 。 这似乎是一个编译器实现错误,而不是一个依赖于实现的细节。B::f

如果添加以下行,则编译正常并选择了正确的函数B::f<>

struct C : public A, public B { 
  using A::f; // optional
  using B::f;
};

[有趣的是,在::f没有被纳入 范围之前C,它们被视为外来函数。]

于 2012-04-02T11:36:57.133 回答
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);
于 2012-04-02T11:25:10.103 回答
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决定它们是模棱两可的。

对于案例类Dint选择版本而不是float因为参数是整数。

于 2012-04-02T11:38:21.063 回答
0

可能发生的情况是模板实例化分别为 classA和发生B,因此以两个void f(int)函数结束。

这不会发生,D因为编译器知道该void f(int)函数是专门化的,因此不专门T用于int.

于 2012-04-02T11:40:44.963 回答