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

时间:2021-05-17 16:46:09
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);
}

What is the reason for which calling d.f is fine, but c.f gives

调用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 个解决方案

#1


11  

The first part is due to member name lookup, that's why it fails.

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

I would refer you to: 10.2/2 Member name lookup

我会推荐你​​:10.2 / 2会员名称查找

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration.

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

If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

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

Now, for the matter with template functions.

现在,关于模板功能的问题。

As per 13.3.1/7 Candidate functions and argument list

根据13.3.1 / 7候选函数和参数列表

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way. A given name can refer to one or more function templates and also to a set of overloaded non-template functions. In such a case, the candidate functions generated from each function template are combined with the set of non-template candidate functions.

在候选者是函数模板的每种情况下,使用模板参数推导(14.8.3,14.8.2)生成候选函数模板特化。然后以通常的方式将这些候选人作为候选职能处理。给定名称可以引用一个或多个函数模板,也可以引用一组重载的非模板函数。在这种情况下,从每个功能模板生成的候选函数与该组非模板候选函数组合。

And if you continue reading 13.3.3/1 Best viable function

如果你继续阅读13.3.3 / 1最佳可行功能

F1 is considered to be a better function, if:

如果出现以下情况,F1被认为是更好的功能:

F1 is a non-template function and F2 is a function template specialization

F1是非模板函数,F2是函数模板特化

That's why the following snippet compiles and runs the non-template function without error:

这就是为什么以下代码片段编译并运行非模板函数而没有错误:

D c;
c.f(1);

#2


1  

I believe the compiler prefers A::f (non-template function) over B::f for no reason.
This seems to be a compiler implementation bug more than a implementation dependent detail.

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

If you add following line, then compilation goes fine and the correct function B::f<> is selected:

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

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

[Funny part is that until the ::f are not brought into the scope of C, they are treated as alien functions.]

[有趣的是,直到:: f没有进入C的范围,它们被视为外来函数。]

#3


0  

A compiler doesn't know which method to call from the C class because templated method will be transormed in void f(int) in case of int type so you have two methods with the same name and same arguments but members of different parent classes.

编译器不知道从C类调用哪个方法,因为在int类型的情况下,模板化方法将在void f(int)中进行转换,因此您有两个具有相同名称和相同参数但不同父类成员的方法。

template<typename T> void f(T x) {} 

or

要么

void f(int)

try this:

尝试这个:

c.B::f<int>(3);

or this for the A class:

或者这个A类:

c.A::f(3);

#4


0  

Consider this simpler example:

考虑这个更简单的例子:

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);
}

In this example, same as yours, D compiles but C does not.
If a class is a derived one, member lookup mechanism behaves different. It checks each base class and merges them: In the case of C; Each base class matches the lookup ( A::f(int) and B::f(float) ). Upon merging them C decides they are ambiguous.

在这个例子中,与你的相同,D编译,但C不编译。如果类是派生类,则成员查找机制的行为会有所不同。它检查每个基类并合并它们:在C的情况下;每个基类都匹配查找(A :: f(int)和B :: f(float))。在合并它们时,C决定它们是模棱两可的。

For the case class D: int version is selected instead of float because parameter is an integer.

对于案例类D:选择int版本而不是float,因为参数是一个整数。

#5


0  

What is probably happening is that the template instantiation is happening separately for class A and B, thus ending in two void f(int) functions.

可能发生的是模板实例化分别针对类A和B发生,因此以两个void f(int)函数结束。

This does not happen in D since there the compiler knows about the void f(int) function as a specialization and therefore does not specialize T for int.

这不会发生在D中,因为编译器知道void f(int)函数是一个特化,因此不会将T专门化为int。

#1


11  

The first part is due to member name lookup, that's why it fails.

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

I would refer you to: 10.2/2 Member name lookup

我会推荐你​​:10.2 / 2会员名称查找

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration.

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

If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

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

Now, for the matter with template functions.

现在,关于模板功能的问题。

As per 13.3.1/7 Candidate functions and argument list

根据13.3.1 / 7候选函数和参数列表

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way. A given name can refer to one or more function templates and also to a set of overloaded non-template functions. In such a case, the candidate functions generated from each function template are combined with the set of non-template candidate functions.

在候选者是函数模板的每种情况下,使用模板参数推导(14.8.3,14.8.2)生成候选函数模板特化。然后以通常的方式将这些候选人作为候选职能处理。给定名称可以引用一个或多个函数模板,也可以引用一组重载的非模板函数。在这种情况下,从每个功能模板生成的候选函数与该组非模板候选函数组合。

And if you continue reading 13.3.3/1 Best viable function

如果你继续阅读13.3.3 / 1最佳可行功能

F1 is considered to be a better function, if:

如果出现以下情况,F1被认为是更好的功能:

F1 is a non-template function and F2 is a function template specialization

F1是非模板函数,F2是函数模板特化

That's why the following snippet compiles and runs the non-template function without error:

这就是为什么以下代码片段编译并运行非模板函数而没有错误:

D c;
c.f(1);

#2


1  

I believe the compiler prefers A::f (non-template function) over B::f for no reason.
This seems to be a compiler implementation bug more than a implementation dependent detail.

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

If you add following line, then compilation goes fine and the correct function B::f<> is selected:

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

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

[Funny part is that until the ::f are not brought into the scope of C, they are treated as alien functions.]

[有趣的是,直到:: f没有进入C的范围,它们被视为外来函数。]

#3


0  

A compiler doesn't know which method to call from the C class because templated method will be transormed in void f(int) in case of int type so you have two methods with the same name and same arguments but members of different parent classes.

编译器不知道从C类调用哪个方法,因为在int类型的情况下,模板化方法将在void f(int)中进行转换,因此您有两个具有相同名称和相同参数但不同父类成员的方法。

template<typename T> void f(T x) {} 

or

要么

void f(int)

try this:

尝试这个:

c.B::f<int>(3);

or this for the A class:

或者这个A类:

c.A::f(3);

#4


0  

Consider this simpler example:

考虑这个更简单的例子:

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);
}

In this example, same as yours, D compiles but C does not.
If a class is a derived one, member lookup mechanism behaves different. It checks each base class and merges them: In the case of C; Each base class matches the lookup ( A::f(int) and B::f(float) ). Upon merging them C decides they are ambiguous.

在这个例子中,与你的相同,D编译,但C不编译。如果类是派生类,则成员查找机制的行为会有所不同。它检查每个基类并合并它们:在C的情况下;每个基类都匹配查找(A :: f(int)和B :: f(float))。在合并它们时,C决定它们是模棱两可的。

For the case class D: int version is selected instead of float because parameter is an integer.

对于案例类D:选择int版本而不是float,因为参数是一个整数。

#5


0  

What is probably happening is that the template instantiation is happening separately for class A and B, thus ending in two void f(int) functions.

可能发生的是模板实例化分别针对类A和B发生,因此以两个void f(int)函数结束。

This does not happen in D since there the compiler knows about the void f(int) function as a specialization and therefore does not specialize T for int.

这不会发生在D中,因为编译器知道void f(int)函数是一个特化,因此不会将T专门化为int。