The following snippet produces an "ambigious call to foo" error during compilation, and I'd like to know if there is any way around this problem without fully qualifying the call to foo:
下面的代码片段在编译过程中产生了一个“对foo的矛盾调用”错误,我想知道在没有完全限定对foo的调用的情况下,是否有任何方法可以解决这个问题:
#include <iostream>
struct Base1{
void foo(int){
}
};
struct Base2{
void foo(float){
}
};
struct Derived : public Base1, public Base2{
};
int main(){
Derived d;
d.foo(5);
std::cin.get();
return 0;
}
So, question is as the title says. Ideas? I mean, the following works flawlessly:
问题就像题目说的。想法吗?我的意思是,以下的方法完美无缺:
#include <iostream>
struct Base{
void foo(int){
}
};
struct Derived : public Base{
void foo(float){
}
};
int main(){
Derived d;
d.foo(5);
std::cin.get();
return 0;
}
3 个解决方案
#1
42
Member lookup rules are defined in Section 10.2/2
成员查找规则在第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 namef
in one sub-objectB
hides a member namef
in a sub-objectA
ifA
is a base class sub-object ofB
. 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 ofC
that is of the type containing the declara-tion designated by the using-declaration. 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.下面的步骤定义类作用域c中的名称查找结果。首先,要考虑类中的名称及其每个基类子对象的每个声明。如果A是B的基类子对象,那么一个子对象B中的成员名f在子对象A中隐藏了一个成员名f。使用声明引入的每个声明都被认为来自C的每个子对象,该子对象的类型包含使用声明指定的声明。如果结果的声明集合不是来自同一类型的子对象,或者该集合有一个非静态成员,并且包含来自不同子对象的成员,那么就会出现歧义,并且程序是病态的。否则,该集合就是查找的结果。
class A {
public:
int f(int);
};
class B {
public:
int f();
};
class C : public A, public B {};
int main()
{
C c;
c.f(); // ambiguous
}
So you can use the using
declarations A::f
and B::f
to resolve that ambiguity
因此,可以使用using声明A::f和B::f来解决这个歧义。
class C : public A, public B {
using A::f;
using B::f;
};
int main()
{
C c;
c.f(); // fine
}
The second code works flawlessly because void foo(float)
is inside C's scope. Actually d.foo(5);
calls void foo(float)
and not the int
version.
第二个代码可以完美地工作,因为void foo(float)位于C的范围内。实际上d.foo(5);调用void foo(float)而不是int版本。
#2
2
Will it work for you?
对你有用吗?
struct Derived : public Base1, public Base2{
using Base2::foo;}
#3
2
Name lookup is a separate phase to overload resolution.
名称查找是重载解析的单独阶段。
Name lookup occurs first. That is the process of deciding which scope the name applies to. In this case we must decide whether d.foo
means d.D::foo
, or d.B1::foo
, or d.B2::foo
. The name lookup rules do not take into account function parameters or anything; it is purely about names and scopes.
发生名称查找。这是决定名称适用于哪个范围的过程。在这种情况下,我们必须决定d。foo是d。D::foo,或者D。B1::foo,或d.B2::foo。名称查找规则不考虑函数参数或其他内容;它纯粹是关于名称和范围的。
Only once that decision has been made, do we then perform overload resolution on the different overloads of the function in the scope where the name was found.
只有在做出了这个决定之后,我们才会在发现名称的范围内对函数的不同重载执行重载解析。
In your example, calling d.foo()
would find D::foo()
if there were such a function. But there is none. So, working backwards up the scopes, it tries the base classes. Now foo
could equally look up to B1::foo
or B2::foo
so it is ambiguous.
在您的示例中,如果有这样一个函数,那么调用d.foo()将会找到D: foo()。但是没有。向后看作用域,它尝试基类。foo可以同样地向上看B1: foo或B2: foo所以它是模糊的。
For the same reason, you would get ambiguity calling unqualified foo(5);
inside a D
member function.
出于同样的原因,您会得到调用非限定foo(5)的歧义;在D成员函数内。
The effect of the recommended solution:
推荐方案的效果:
struct Derived : public Base1, public Base2{
using Base1::foo;
using Base2::foo;
is that this creates the name D::foo
, and makes it identify two functions. The result is that d.foo
resolves to d.D::foo
, and then overload resolution can happen on these two functions that are identified by D::foo
.
是这样创建的名称D::foo,并使它识别两个函数。结果是d。foo解析为d。D::foo,然后重载解析可以发生在D: foo识别的这两个函数上。
Note: In this example D::foo(int)
and Base1::foo(int)
are two identifiers for the one function; but in general, for the name lookup and overload resolution process, it doesn't make a difference whether they are two separate functions or not.
注意:在本例中D::foo(int)和Base1:::foo(int)是一个函数的两个标识符;但是一般来说,对于名称查找和重载解析过程来说,它们是否是两个独立的函数并不重要。
#1
42
Member lookup rules are defined in Section 10.2/2
成员查找规则在第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 namef
in one sub-objectB
hides a member namef
in a sub-objectA
ifA
is a base class sub-object ofB
. 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 ofC
that is of the type containing the declara-tion designated by the using-declaration. 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.下面的步骤定义类作用域c中的名称查找结果。首先,要考虑类中的名称及其每个基类子对象的每个声明。如果A是B的基类子对象,那么一个子对象B中的成员名f在子对象A中隐藏了一个成员名f。使用声明引入的每个声明都被认为来自C的每个子对象,该子对象的类型包含使用声明指定的声明。如果结果的声明集合不是来自同一类型的子对象,或者该集合有一个非静态成员,并且包含来自不同子对象的成员,那么就会出现歧义,并且程序是病态的。否则,该集合就是查找的结果。
class A {
public:
int f(int);
};
class B {
public:
int f();
};
class C : public A, public B {};
int main()
{
C c;
c.f(); // ambiguous
}
So you can use the using
declarations A::f
and B::f
to resolve that ambiguity
因此,可以使用using声明A::f和B::f来解决这个歧义。
class C : public A, public B {
using A::f;
using B::f;
};
int main()
{
C c;
c.f(); // fine
}
The second code works flawlessly because void foo(float)
is inside C's scope. Actually d.foo(5);
calls void foo(float)
and not the int
version.
第二个代码可以完美地工作,因为void foo(float)位于C的范围内。实际上d.foo(5);调用void foo(float)而不是int版本。
#2
2
Will it work for you?
对你有用吗?
struct Derived : public Base1, public Base2{
using Base2::foo;}
#3
2
Name lookup is a separate phase to overload resolution.
名称查找是重载解析的单独阶段。
Name lookup occurs first. That is the process of deciding which scope the name applies to. In this case we must decide whether d.foo
means d.D::foo
, or d.B1::foo
, or d.B2::foo
. The name lookup rules do not take into account function parameters or anything; it is purely about names and scopes.
发生名称查找。这是决定名称适用于哪个范围的过程。在这种情况下,我们必须决定d。foo是d。D::foo,或者D。B1::foo,或d.B2::foo。名称查找规则不考虑函数参数或其他内容;它纯粹是关于名称和范围的。
Only once that decision has been made, do we then perform overload resolution on the different overloads of the function in the scope where the name was found.
只有在做出了这个决定之后,我们才会在发现名称的范围内对函数的不同重载执行重载解析。
In your example, calling d.foo()
would find D::foo()
if there were such a function. But there is none. So, working backwards up the scopes, it tries the base classes. Now foo
could equally look up to B1::foo
or B2::foo
so it is ambiguous.
在您的示例中,如果有这样一个函数,那么调用d.foo()将会找到D: foo()。但是没有。向后看作用域,它尝试基类。foo可以同样地向上看B1: foo或B2: foo所以它是模糊的。
For the same reason, you would get ambiguity calling unqualified foo(5);
inside a D
member function.
出于同样的原因,您会得到调用非限定foo(5)的歧义;在D成员函数内。
The effect of the recommended solution:
推荐方案的效果:
struct Derived : public Base1, public Base2{
using Base1::foo;
using Base2::foo;
is that this creates the name D::foo
, and makes it identify two functions. The result is that d.foo
resolves to d.D::foo
, and then overload resolution can happen on these two functions that are identified by D::foo
.
是这样创建的名称D::foo,并使它识别两个函数。结果是d。foo解析为d。D::foo,然后重载解析可以发生在D: foo识别的这两个函数上。
Note: In this example D::foo(int)
and Base1::foo(int)
are two identifiers for the one function; but in general, for the name lookup and overload resolution process, it doesn't make a difference whether they are two separate functions or not.
注意:在本例中D::foo(int)和Base1:::foo(int)是一个函数的两个标识符;但是一般来说,对于名称查找和重载解析过程来说,它们是否是两个独立的函数并不重要。