【问题标题】:cannot access namespace scope friend explicitly无法显式访问命名空间范围的朋友
【发布时间】:2017-12-19 14:45:44
【问题描述】:

我今天遇到一个问题,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,但我惊讶地发现我无法通过使用命名空间 foo 来访问 str

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::,而我却找不到它?

【问题讨论】:

  • 第一个例子和第二个例子有很大的不同:在第一个例子中,函数str仍然是一个类的成员foo::Foo,它不是一个非成员函数,不能这样调用。在第二个示例中,str 函数非成员函数。
  • @Someprogrammerdude 是的,但是为什么我不能调用foo::str,因为strfoo 命名空间中的朋友函数?
  • @Someprogrammerdude 你会注意到我没有尝试访问foo::Foo::str(即:好像它是一个成员),而是我试图访问foo::str(即:作为如果它是 foo 命名空间内的函数)
  • 如果你定义了一个friend 非成员函数inline,它实际上是在类中作用域的。 __PRETTY_FUNCTION__ 并没有说这可能是编译器的问题。
  • 刚才链接的答案中没有提到你问的情况吗?

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


【解决方案1】:
  • 为什么我无法找到 str(..) 使用封闭命名空间显式限定它的范围?

来自标准,[namespace.memdef]/3

如果非本地类中的友元声明首先声明了一个类, 朋友是成员的函数、类模板或函数模板 最里面的封闭命名空间。朋友声明不 本身使名称对不合格的查找或合格的查找可见 抬头。 [ 注意:朋友的名字会显示在 如果在命名空间范围内提供了匹配的声明,则命名空间 (在授予友谊的类定义之前或之后)。 — 尾注 ]

这意味着str 在名称查找中不可见;只能通过 ADL 调用。

  • 为什么__PRETTY_FUNCTION__ 说它在foo::,而我却找不到它?

来自[class.friend]/6

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

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

来自cppreference.com的解释:

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

【讨论】:

    猜你喜欢
    • 2018-10-20
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    • 2020-01-17
    • 2018-06-24
    相关资源
    最近更新 更多