无法显式访问命名空间范围的朋友

时间:2017-12-19 14:45:45

标签: c++ language-lawyer friend argument-dependent-lookup name-lookup

今天我遇到了一个问题,即ADL没有为类中定义的类型找到静态成员函数。

也就是说,在下面的示例中,str(foo::Foo::Enum)并未通过ADL定位而未明确确定范围,foo::Foo::str(foo::Foo::Enum)

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    static const char* str(Enum e);
};

}

foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = str(e);              // ADL doesn't work

我发现了this SO问题,并且如接受的答案中所述,将其更改为friend函数会导致ADL正在运行。

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    friend const char* str(Enum e);  // note str is now a friend
};

}

foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = str(e);              // ADL works now

虽然这现在有助于ADL,但我惊讶地发现我无法通过使用命名空间str

来访问foo
foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = foo::str(e);         // error: ‘str’ is not a member of ‘foo’

我进行了测试,在那里打印出__PRETTY_FUNCTION__的结果,更令人惊讶的是看到str的范围显然是foo::

__PRETTY_FUNCTION__: const char* foo::str(foo::Foo::Enum)

以下工作示例:

#include <iostream>

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    friend const char* str(Enum e)
    {
        return __PRETTY_FUNCTION__;
    }
};

}

int main()
{
    foo::Foo::Enum e = foo::Foo::FOO1;

    std::cout << str(e) << '\n';
    // std::cout << foo::str(e) << '\n'; // error: ‘str’ is not a member of ‘foo’

    return 0;
}

输出:

$ ./a.out
const char* foo::str(foo::Foo::Enum)

问题:

  • 为什么我无法找到str(..)使用封闭的命名空间显式确定范围?
  • 为什么__PRETTY_FUNCTION__foo::中说出来,但我无法找到它?

1 个答案:

答案 0 :(得分:2)

  
      
  • 为什么我无法找到str(..)使用封闭的命名空间显式确定范围?
  •   

从标准[namespace.memdef]/3

开始
  

如果非本地类中的朋友声明首先声明一个类,   朋友是其成员的函数,类模板或函数模板   最里面的封闭命名空间。朋友声明没有   本身使名称对于不合格的查找或限定可见   抬头。 [注意:朋友的名字将在其中显示   如果在命名空间范围内提供匹配的声明,则为namespace   (在授予友谊的班级定义之前或之后)。    - 结束说明]

这意味着名称查找不会显示str;它只能通过ADL调用。

  
      
  • 为什么__PRETTY_FUNCTION__会在foo::中说出来,但我无法找到它?
  •   

来自[class.friend]/6

  

当且仅当该类是非本地类([class.local]),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数。

str确实成为命名空间foo的成员;它只是看不见。

来自cppreference.com的解释:

  

非本地类X中由friend声明引入的名称成为X的最内层封闭命名空间的成员,但它们不会对lookup可见(既不合格也不合格),除非匹配声明在命名空间范围内提供,在类定义之前或之后。这样的名称可以通过ADL找到,它同时考虑名称空间和类。