我有下一个代码:
void f(int){}
struct A
{
void f()
{
f(1);
}
};
此代码格式不正确,错误消息(GCC):error: no matching function for call to ‘A::f(int)’
或(clang)Too many arguments to function call, expected 0, have 1; did you mean '::f'?
为什么我需要使用::
来调用与成员函数同名但具有不同签名的非成员函数?这个要求的动机是什么?
我认为编译器应该能够弄清楚我想要调用非成员函数,因为签名是不同的(clang甚至将它放在错误消息中!)。
请不要将此标记为重复 - 这是与Calling in C++ a non member function inside a class with a method with the same
不同的问题答案 0 :(得分:21)
为什么我需要使用::来调用与成员函数同名但具有不同签名的非成员函数
因为那些是规则。嵌套范围中的名称会在更广泛的范围内隐藏具有相同名称的实体。
这项要求的动机是什么?
考虑一个成员函数调用另一个成员并且签名不匹配的情况:
struct A {
void f(double);
void g() {f(42);} // requires int->double conversion
};
现在假设有人在周围的命名空间中添加了一个不相关的函数
void f(int);
如果这包含在A
范围内的重载集中,那么A::g
的行为会突然改变:它会调用此而不是A::f
。将过载集限制在最窄可用范围内的名称可以防止出现这种意外破坏。
正如您(以及您的有用编译器)所说,如果您需要,外部名称仍然可用(具有资格)。
答案 1 :(得分:8)
编译器为f
执行非限定名称查找,在§3.4.1[basic.lookup.unqual]中指定(幸运的是,这里没有ADL):
1在3.4.1中列出的所有情况下,搜索范围为a 按各自类别列出的顺序进行声明; 一旦找到名称的声明,名称查找就会结束。如果不 声明被发现,该程序是不正确的。
8对于类
X
的成员,在成员函数中使用的名称 body,在默认参数中,在 exception-specification 中,在 brace-or-equal-initializer 非静态数据成员(9.2),或者在X定义之外的类成员的定义中, 在成员的 declarator-id 之后,应在其中一个中声明 以下方式:
- 在使用它之前或在封闭块(6.3)或
中使用之前- 应该是类
X
的成员,或者是X
(10.2)的基类成员,或者- 如果
X
是类Y
(9.7)的嵌套类,则应为Y
的成员,或者应为Y
基类的成员(此查找适用 反过来到Y
的封闭类,从最里面开始 封闭类)或- 如果
X
是本地类(9.8)或是本地类的嵌套类,则在包含定义的块中定义类X
之前 班级X
,或- 如果
X
是名称空间N
的成员,或者是N
成员的类的嵌套类,或者是本地类或嵌套类 在使用之前,是N
成员的函数的本地类 名称,在名称空间N
或N
的一个名称空间中。
一旦找到声明,名称查找就会停止。因此,一旦它在第二个项目符号点找到成员f()
,它就会停止,从不在其他地方搜索。
在重载解析后,在名称查找后拒绝拒绝不可行的函数。
答案 2 :(得分:1)
为什么我需要使用::来调用与成员函数同名的非成员函数,但具有不同的签名?这个要求的动机是什么?
这就是拥有命名空间的重点。本地(更近距离)的名称是首选,并且在全局名称上更明显。由于struct
又是范围,因此f
会影响::f
的可见性。当你拥有全球性的时候,你可以说你做到了。为什么呢?
这是作为一项功能提供的,以确保您可以安静地调用您定义的函数,假设它们将被调用,并且当您需要来自不同命名空间的函数(例如标准库)时,您明确说明,例如std::
。它只是一种清晰的消除歧义形式,没有机会发挥其作用。
答案 3 :(得分:0)
要了解错误的原因以及为什么需要显式使用::f()
语法,您可能需要考虑C ++编译器进程的某些方面:
编译器首先做的是名称查找。
非限定名称查找从当前范围开始,然后向外移动;它会在找到函数名称的声明后立即停止,即使此函数稍后将被确定为不是函数调用的可行候选者。
然后,对名称查找找到的函数集执行重载解析。
(最后,访问检查会在超载分辨率获取的函数上执行。)