关于友元函数定义和命名空间范围

Dan*_*nte 8 c++ friend argument-dependent-lookup

我正在阅读这篇博客文章部分,并尝试使用提供的代码段.

namespace N {
// 2
class A {
friend void f(A) {} // 1
};
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,那么定义// 1将注入名称f所在// 2的位置.但是它只能通过参数依赖查找来获得.精细.

帖子里有一句引起我注意的句子:

7.3.1.2/3命名空间成员定义[namespace.memdef] p3

首先在名称空间中声明的每个名称都是该名称空间的成员.如果非本地类中的友元声明首先声明了类,函数,类模板或函数模板,则该友元是最内层封闭命名空间的成员.友元声明本身不会使名称对非限定查找(3.4.1)或限定查找(3.4.3)可见.

请注意,没有任何地方声明,友元声明引入的名称必须与声明和/或定义的类的名称有任何特定的关系,或者与该类的任何特定关系(就此而言).

由此,我认为以下代码段是有效的:

namespace N {
struct A {
};

struct B {
  friend void f(A) {
}
};

int main() {
  N::A a;
  f(a);
}
Run Code Online (Sandbox Code Playgroud)

但它被GCC7和Clang 4都拒绝了.

t.cpp:19:3:错误:在此范围内未声明'f'

有趣的是,当我尝试f使用N::B对象调用时,我收到以下错误:

t.cpp:12:6:错误:无法将'b'从'N :: B'转换为'N :: A'

所以这是我的问题:

f(A)应该通过ADL检测到?由于这两个类都在命名空间中,我不明白为什么会失败.我查看了标准关于朋友的部分,但未找到相关部分.

我想知道f(A)注入了哪个范围,因为当我尝试通过调用给出错误的参数类型时,GCC能够找到它f(B).

Vit*_*meo 3

cppreference/cpp/language/friend

首先在类或类模板中的友元声明中声明的名称X将成为 的最内层封闭命名空间的成员X,但无法进行查找(除了考虑 的参数相关查找X),除非提供了命名空间范围内的匹配声明 - 请参阅命名空间以获取详细信息。


cppreference/cpp/language/namespace

非局部类中由友元声明引入的名称X将成为 的最内层封闭命名空间的成员X,但它们对查找不可见(既不是非限定的也不是限定的),除非在命名空间范围内(在类之前或之后)提供了匹配的声明定义。这样的名称可以通过考虑命名空间和类的 ADL 找到。


这与您的示例一致 -f采用 an A,它与封闭类的类型不同。

如果您将示例更改为...

namespace N {
struct A {
};

struct B {
  friend void f(B) {
}
};

int main() {
  N::B b;
  f(b);
}
Run Code Online (Sandbox Code Playgroud)

...它将编译。


相关标准报价:

$14.3 [班级.朋友]

类的友元是被授予使用类中的私有和受保护成员名称的权限的函数或类。[...] 当且仅当该类是非本地类 ([class.local])、函数名未限定且函数具有命名空间作用域时,才可以在类的友元声明中定义函数。[...] 这样的函数隐式地是一个内联函数。类中定义的友元函数位于定义它的类的(词法)范围内。在类外部定义的友元函数不是 ([basic.lookup.unqual])。

  • 这是有道理的,但我仍然不清楚一件事。“struct A”的“最内层封闭命名空间”不就是命名空间“N”吗?或者编译器会生成隐藏的命名空间吗?这可以解释为什么 ADL 在我的示例中不起作用。或者可能是因为 ADL 规则中的特殊情况? (2认同)