哪些规范性文本规定了在非ADL查找中只能在封闭的名称空间中声明一次重新才能找到一个在类中定义的朋友? 问题朋友声明无论是否定义不会在封闭的名称空间中引入新名称

问题描述

下面的所有标准参考均引用N4659: March 2017 post-Kona working draft/C++17 DIS


声明为好友的函数也可以在好友声明中定义,如下所示:

#include <iostream>

namespace a {
struct A {
    // Definition of 'a::foo()' 
    friend void foo() { std::cout << __PRETTY_FUNCTION__; }   
};
}  // namespace a

诸如a::foo()之类的功能(有时称为“隐藏的朋友”)无法通过不合格或合格的查找来找到:

int main() {
    a::foo();  // error: no member named 'foo' in namespace 'a'
}

根据[basic.lookup.argdep]/4可以找到,但是可以通过ADL [强调我的]:

考虑关联命名空间时,查找与 当关联的名称空间用作 限定符([namespace.qual]),除了:

  • [...]
  • 在关联类中声明的任何命名空间范围的朋友功能或朋友功能模板 相应的名称空间,即使它们在普通情况下不可见 查找([class.friend])。
  • [...]

专门基于ADL定义的类类型:

#include <iostream>

namespace a {
struct A {
    friend void foo(const A&) { std::cout << __PRETTY_FUNCTION__; }   
};
}  // namespace a

int main() {
    foo(a::A{});  // void a::foo(const a::A &)
}

但是,如果我们在定义它的类的名称空间范围内(重新)声明该函数,则可以通过限定/不限定查找来找到该函数:

#include <iostream>

namespace a {
struct A {
    friend void foo() { std::cout << __PRETTY_FUNCTION__; }   
};
void foo();
}  // namespace a

int main() {
    a::foo();  // void a::foo()
}

问题

  • 哪些规范性文本规定了在非ADL查找中只能在封闭的名称空间中声明一次(重新)才能找到一个类中定义的朋友?

解决方法

下面的所有标准参考,除非另有明确说明,请参考N4659: March 2017 post-Kona working draft/C++17 DIS


朋友声明(无论是否定义)不会在封闭的名称空间中引入新名称

[class.friend]/6控制“隐藏的朋友”的范围(仅在其朋友声明中声明和定义)[强调我的]:

[class.friend] / 6:当且仅当该类是非本地类([class.local])时,才可以在该类的朋友声明中定义一个函数,函数名称不合格,且函数具有名称空间范围。

因此,即使函数的定义位于其朋友声明中,该朋友函数也具有名称空间范围,并且可以通过(在其所在类的)命名空间中的(重新)声明来显式地进行常规查找。已定义。)

#include <iostream>

namespace a {
struct A {
    // Definition of 'a::foo()' (note the namespace scope).
    friend void foo() { std::cout << __PRETTY_FUNCTION__; }   
};

// (re-)declaration of foo().
void foo();
}  // namespace 'a'

int main() {
    a::foo();  // void a::foo()
}

但是,要问的更普遍和有趣的问题是

  • 什么决定了朋友声明不将声明的名称引入/绑定到命名空间范围?

哪个受[namespace.memdef]/3约束

[namespace.memdef] / 3: 如果在非本地类中的朋友声明首先声明一个类,函数,类模板或函数模板朋友是最里面的封闭命名空间的成员。朋友声明本身不会使名称显示给不合格的查找([basic.lookup.unqual])或合格的查找([basic.lookup.qual])。 [注意: 如果在名称空间范围内提供了匹配的声明,则朋友的名称将在其名称空间中可见(在授予友谊的类定义之前或之后)。 —注释] [...]

因此,此处的功能不是在好友声明中定义,而是在好友声明(即使不包含定义)中也不会将名称引入名称空间范围;在以下示例中,查找同样无法从好友声明中找到名称:

#include <iostream>

namespace a {
struct A {
    friend void bar(); 
};
}  // namespace 'a'

int main() {
    a::bar();  // error: no member named 'bar' in namespace 'a'
}

我们还可能注意到,C ++ 20草案N4861 2020年3月布拉格后工作草案/ C ++ 20 DIS )添加了一个段落,尽管不是规范,明确提到这一点;来自[basic.scope.pdecl]/13 [强调我的]:

[注意:朋友声明是指最近的封闭命名空间成员的函数或类,但它们不会在该命名空间中引入新名称([命名空间。 memdef])。 [...]

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...