我有以下简化代码
namespace Namespace
{
int foo() { return 1; }
class Class
{
public:
int foo() const { return 2; }
class Nested {
public:
Nested()
{
cout << foo() << endl;
}
};
};
}
我收到了这个错误:
错误:无法调用成员函数'int Namespace :: Class :: foo()const' 没有对象:
cout << foo() << endl;
^^^^^
似乎编译器选择非静态int Namespace::Class::foo() const
而不是全局函数int Namespace::foo()
。
但是如何能够在没有对象的情况下调用来自其他类的非静态函数呢? Nested object
无法访问周围的Class object
- 毕竟这不是Java。
我仔细阅读了overload resolution from cppreference我无法找到这种行为的理由。我更怀疑这是gcc错误。
第二个问题的答案。解决方法很简单,需要告诉编译器存在这样的全局函数:
Nested()
{
using Namespace::foo; //< workaround: inform compiler such function exists
cout << foo() << endl;
}
顺便说一下,解决方法是否正确?有没有更好的解决方案?
答案 0 :(得分:5)
我仔细阅读了来自cppreference的重载解析我无法找到这种行为的基本原理。你能指出导致这种行为的语言规则吗?
在重载解析过程选择最佳可行功能之前,会在名称查找阶段生成一组初始候选项。换句话说,应在Name lookup部分搜索预期的行为,而不是Overload resolution部分。
C ++标准中描述了非限定名称的名称查找过程:
§3.4.1[basic.lookup.unqual] / p8:
在函数的 declarator-id 之后或大括号中的类
X
的成员函数(9.3)的定义中使用的名称类X
的非静态数据成员(9.2)的-or-equal-initializer 应以下列方式之一声明:- 在使用它的块中或在封闭块(6.3)中使用之前,或
- 应该是X类成员或者是X(10.2)基类的成员,或者
- 如果
X
是类Y
(9.7)的嵌套类,则应为Y
的成员,或者是{的成员Y
的基类 (这个查找依次应用于Y
的封闭类,从最里面的封闭类开始,或者[...]
并且只有在仍未找到的情况下:
- 如果
X
是名称空间N
的成员,或是属于N
成员的类的嵌套类,或者是本地类或属于N
成员的函数的本地类中的嵌套类,在使用名称之前,在命名空间N
或其中一个{ {1}}包含名称空间。
由于名称查找在找到名称后立即结束(§3.4.1[basic.lookup.unqual] / p1):
在3.4.1中列出的所有情况下,在每个相应类别中列出的顺序中搜索范围;一旦找到名称的声明,名称查找就会结束。
在您遇到N
时,不会搜索其他范围。
解决方法很简单,需要告诉编译器存在这样的全局函数:
int foo() const { return 2; }
这种解决方法是否正确?
§7.3.3[namespace.udecl] / p1:
using-declaration 在声明区域中引入了一个名称, using-declaration 出现在该声明区域中。
§3.3.1[basic.scope.declarative] / p1:
程序文本的某些部分引入了每个名称,称为声明区域,这是该名称有效的程序的最大部分,即其中该名称可用作非限定名称以引用同一实体。
使用 using-declaration 引入名称会影响非限定名称查找,使其在第一步找到该函数,即该名称变为声明:
- 在使用它的块中或在封闭块(6.3)中使用之前
有没有更好的解决方案?
在引用某个命名空间作用域中的函数时,可以使用限定名称,明确指出要引用的符号:
using Namespace::foo; //< workaround: inform compiler such function exists
答案 1 :(得分:4)
这确实是关于Name Lookup而不是Overload Resolution。
事实上,正如标准规定的那样,N3376(强调我的)§3.4.1/ 1 :
在3.4.1中列出的所有情况下,在每个相应类别中列出的顺序中搜索范围;一旦找到名称的声明,名称查找就会结束。如果没有找到声明,程序就会格式不正确。
事实上,只要找到声明,查找就会停止。
然后,您可以在§3.4.1/ 8 中找到有关您正在处理的案例的更多信息,该案例处理成员函数定义中类中使用的名称,尤其是此部分:
如果X是类Y(9.7)的嵌套类,则应该是Y的成员,或者应该是Y的基类的成员(此查找依次应用于Y的封闭类,从最内层封闭开始类)
如果没有找到,请附上名称空间。
在您的情况下,这意味着Namespace::Class::foo()
是查找过程中找到的第一个名称,一旦找到名称就会停止,因此甚至不会考虑Namespace::foo()
。
标准给出的示例说明了查找路径:
class B { };
namespace M {
namespace N {
class X : public B {
void f();
};
}
}
void M::N::X::f() {
i = 16;
}
搜索以下范围以获取i:
的声明1)M :: N :: X :: f的最外层块范围,在使用i // 2之前,类M :: N :: X的范围
3)M :: N :: X基类B的范围
4)命名空间M :: N
的范围5)命名空间M的范围
6)全局范围,在M :: N :: X :: f
的定义之前
至于你如何处理这个问题,你必须对这个电话进行限定,即
cout << Namespace::foo() << endl;
以便编译器通过限定查找选择所需的函数。
答案 2 :(得分:2)
Piotr S.和JBL解释了你问题的原因。
恕我直言,更简单的解决方案是使用函数的限定名称:
public:
Nested()
{
cout << Namespace::foo() << endl;
}