请考虑[namespace.memdef]/3中的以下子句:
如果
friend声明中的名称既不是限定名也不是模板标识,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何范围.
是否有理由将template-id与限定名称一起使用?就此而言,是否有理由查找非限定为最内层封闭命名空间的模板ID的非限定名称?是否存在本条款解决的特定问题或用例?
限定名称和模板ID不能将新成员引入封闭命名空间,这就是[namespace.memdef] p3中的注释试图说:
[ 注意:其他形式的
friend声明不能声明最内层封闭命名空间的新成员,因此遵循通常的查找规则.- 结束说明 ]
因此,限定名称和模板ID不需要这样的限制.
请注意,template-id缺少模板参数的声明,而qualified-id可能会将名称引入远程无关的命名空间.
这部分答案仍然不完整,但代表了当前的"研究"状态.随意贡献.
由于N0783 - 名称空间问题和提议的解决方案 "试图澄清当前未定义或未完全指定的许多命名空间问题",因此引入了限制(可能是?).
1995年的这篇论文包含两个有关通过朋友声明引入的实体声明相关问题的启发性讨论.请记住,当时的名称查找规则是不同的:
(*)我能找到的最好的是1996年3月的N0878,其中说"最近对工作文件进行了修改,以添加"Koenig查找规则""
首先,来自N0783的功能示例:
Run Code Online (Sandbox Code Playgroud)void f(char); namespace A { class B { friend void f(char); // ::f(char) is a friend friend void f(int); // A::f(int) is a friend void bf(); }; void B::bf() { f(1); // calls A::f(int); f('x'); // also calls A::f(int) because ::f is hidden } }
第二个朋友声明必须引入一个新功能.N0783尝试指定引入此声明的范围.它表明
给定名称的所有好友声明必须在一个特定范围内声明实体.
作为一般规则,避免上述情况的惊喜.
所以问题是,他们在哪个范围内声明实体?也有两种可能性
- 在查找函数的先前声明时,请查看到达最近的封闭命名空间,或者
- 在查找先前的声明时,请查看所有声明的函数名称的封闭范围.如果找到以前使用的名称,则将声明注入该范围.如果未找到先前使用的名称,则将该朋友注入最近的封闭命名空间范围.
规则#2意味着
f在封闭范围内调用的任何函数的存在,无论类型是否匹配,都足以导致朋友声明注入该范围.我认为规则#2显然是不可接受的.命名空间中的友元声明将受到该名称的任何全局声明的影响.考虑这对操作员功能意味着什么!全局范围内任何
operator+函数的存在都会迫使所有朋友operator+操作符也出现在全局范围内!在全局范围中存在模板将具有相同的效果.
对于班级类型:
Run Code Online (Sandbox Code Playgroud)namespace N { class A { void f(); }; } using namespace N; namespace M { class B { friend class A; // Without this rule // makes N::A a friend B(); }; class A { void f(); }; } void N::A::f() { M::B b; } // A friend under current rules void M::A::f() { M::B b; } // A friend under proposed rules
根据当前规则,这两个示例都不那么有趣,因为通过朋友声明引入的名称只能通过ADL找到.这种限制可能是一种历史人工制品.在引入ADL之后,需要更多的"研究"来遵循这一限制的发展.