在当前的C ++标准草案(2019年3月)中,[class.friend] p.6声明(强调我的):
当且仅当该类是非本地类([class.local]),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数。 [...]
“函数具有命名空间范围”是什么意思?
我能想出的唯一一个函数没有命名空间范围的情况如下:
struct A
{
static void f();
struct B
{
friend void f() {};
};
};
但是,clang和gcc都不会将B
中的朋友定义与A
中的静态方法相关联,而是与属于全局命名空间的函数相关联。
我还缺少其他情况吗?
我认为你已经回答了自己的问题,但没有意识到。
“函数具有命名空间范围”意味着它是命名空间的一部分,而不是类或结构的一部分。所以函数A :: B :: f()不存在。并且它也没有引用A :: f()。相反,您定义为朋友的函数实际上是函数:: f(),因为它是它所驻留的命名空间(全局命名空间)。
我怀疑(但尚未尝试过),如果你将所有这些包装在一个命名空间中,那么你定义的f()将是该命名空间的一部分。所以,例如,
namespace ns {
struct A
{
static void f();
struct B
{
friend void f() {};
};
};
}
将friend函数定义为函数ns :: f()。
寻找相关规则,说明一个非限定函数名称的friend
定义始终是命名空间成员,我发现情况并非如此。 [namespace.memdef] / 3:
如果非本地类中的
friend
声明首先声明了类,函数,类模板或函数模板,则该朋友是最内层封闭命名空间的成员。 ...如果friend
声明中的名称既不是限定的也不是模板ID,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑除此之外的任何作用域。最里面的封闭命名空间。
有问题的要求仅适用于函数定义,并排除本地友好类或合格的朋友名称。但这留下了模板ID作为函数名称的可能性。
所以措辞似乎对这段代码产生了影响:
struct A {
template <typename T>
static void f();
template <typename T>
struct B {
friend void f<T>() {}
};
};
在这里,friend声明中的名称是template-id,因此关于跳过非命名空间作用域的规则不适用,f
确实命名了函数模板A::f
。所以[class.friend] / 6表示这是不正确的,但如果friend
声明不是一个定义,它将是良好的形式。