两个超类具有相同名称但不同签名的成员函数时不明确

时间:2012-04-02 11:08:15

标签: c++ templates class-hierarchy

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)

5 个答案:

答案 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)

可能发生的情况是模板实例化是针对类AB单独进行的,因此以两个void f(int)函数结束。

这不会发生在D中,因为编译器知道void f(int)函数是专门化的,因此不会T int专门化。{/ p>