嵌套类奇怪的函数查找:周围的类函数隐藏全局函数

Pio*_*ycz 8 c++ gcc argument-dependent-lookup

我有以下简化代码

namespace Namespace
{
int foo() { return 1; }

class Class
{
public:
    int foo() const { return 2; }
    class Nested {
    public:
        Nested()
        {
            cout << foo() << endl;
        }
    };
};
}
Run Code Online (Sandbox Code Playgroud)

我收到了这个错误:

错误:无法调用成员函数'int Namespace :: Class :: foo()const'没有对象:

 cout << foo() << endl;
         ^^^^^
Run Code Online (Sandbox Code Playgroud)

似乎编译器选择非静态int Namespace::Class::foo() const而不是全局函数int Namespace::foo().

但是如何能够在没有对象的情况下调用来自其他类的非静态函数呢?Nested object无法访问周围环境 Class object- 毕竟这不是Java.

我通过cppreference的重载解析仔细阅读,我找不到这种行为的基本原理.我更怀疑这是gcc错误.

  • 你能指出导致这种行为的语言规则吗?
  • 你如何处理这些问题?

[UPDATE]

只是第二个问题的答案.解决方法很简单,需要告诉编译器存在这样的全局函数:

        Nested()
        {
            using Namespace::foo; //< workaround: inform compiler such function exists
            cout << foo() << endl;
        }
Run Code Online (Sandbox Code Playgroud)

顺便说一下,解决方法是否正确?还有更好的解决方案吗?

Pio*_*cki 5

我通过cppreference的重载解析仔细阅读,我找不到这种行为的基本原理.你能指出导致这种行为的语言规则吗?

在重载解析过程选择最佳可行函数之前,在名称查找阶段期间生成初始候选集.换句话说,应在Name查找部分中搜索预期的行为,而不是在Overload resolution one中搜索.

C++标准中描述了非限定名称的名称查找过程:

§3.4.1[basic.lookup.unqual]/p8:

X在函数的declarator-id之后的类的成员函数(9.3)的定义中或在类的非静态数据成员(9.2)的brace-or-equal-initializer中使用的名称X 应在其中一个中声明.以下方式:

- 在其使用的块中或在封闭块(6.3)中使用之前,或

- 应为X类成员或X(10.2)基类的成员,或

- 如果X是类Y(9.7)的嵌套类,则应该是Y基类的成员,或者应该是基类的成员Y (此查找依次应用于Y封闭类,从最内层的封闭类开始),或者[ ...]

并且只有在仍未找到的情况下:

-如果X是命名空间中的一员N,或者是一个嵌套类一类是的成员N,或者是局部类或局部类的函数是成员内的嵌套类N,使用名称的前,在命名空间N或其中一个N封闭的命名空间中.

由于名称查找在找到名称后立即结束(§3.4.1[basic.lookup.unqual]/p1):

在3.4.1中列出的所有情况下,在每个相应类别中列出的顺序中搜索范围; 一旦找到名称的声明,名称查找就会结束.

在您的情况下,int foo() const { return 2; }遇到任何其他范围都不会被搜索.


解决方法很简单,需要告诉编译器存在这样的全局函数:

using Namespace::foo; //< workaround: inform compiler such function exists
Run Code Online (Sandbox Code Playgroud)

这种解决方法是否正确?

§7.3.3[namespace.udecl]/p1:

甲using声明引入了一个名称到其中所述声明性区域使用声明出现.

§3.3.1[basic.scope.declarative]/p1:

每个名称都在程序文本的某些部分中引入,称为声明性区域,该声明区域是该名称有效的程序的最大部分,也就是说,该名称可以用作非限定名称以引用同一实体.

引入带有using声明的名称会影响非限定名称查找,使得它在第一步中找到该函数,即该名称变为声明:

- 在其使用的块中或在封闭块中使用之前(6.3)


还有更好的解决方案吗?

在从某个命名空间作用域引用函数时,可以使用限定名称,明确指出要引用的符号:

Nested()
{
    cout << Namespace::foo() << endl;
}
Run Code Online (Sandbox Code Playgroud)