C++ 17 友元函数声明和内联命名空间
C++ 17 friend function declaration and inline namespace
考虑以下程序
#include <iostream>
namespace N1
{
inline namespace N2
{
class A
{
public:
friend void f( const A & );
private:
int x = 10;
};
//void f( const A & );
}
void N2::f( const A &a ) { std::cout << a.x << '\n'; }
}
int main()
{
using namespace N1;
A a;
f( a );
}
根据 C++ 17 标准(10.3.1.2 命名空间成员定义)
3 If a friend declaration in a non-local class first declares a class,
function, class template or function template97 the friend is a member
of the innermost enclosing namespace. The friend declaration does not
by itself make the name visible to unqualified lookup (6.4.1) or
qualified lookup (6.4.3).
所以友元函数的名称在命名空间N2中是不可见的。所以它在命名空间 N1 中也应该是不可见的。
但是,clang HEAD 10.0.0.
编译和执行代码时没有警告
编译器gcc HEAD 10.0.0 20191发出警告
prog.cc:18:10: warning: 'void N1::N2::f(const N1::N2::A&)' has not been declared within 'N1::N2'
18 | void N2::f( const A &a ) { std::cout << a.x << '\n'; }
| ^~
prog.cc:10:25: note: only here as a 'friend'
10 | friend void f( const A & );
| ^
但运行程序并输出了正确的结果。
Visual C++ 2019 也成功编译代码,没有警告,程序输出预期结果。
是不是三个编译器的bug,因为名字f
是不可见的,所以封闭命名空间中友元函数f
的定义不正确?
正如我在评论中提到的,f
的函数体和参数并不真正相关,因为问题仅与 N2::f
的名称查找有关。删除它们后,命名空间 N2
是否为 inline
也无关紧要。在这两种情况下,所有编译器的行为方式都相同。
GCC 发出警告,但在 N2::f
的定义中给出 -pedantic-errors
硬错误。 MSVC 和 Clang 始终接受未经诊断的代码。
我认为你是对的,函数定义声明符中的措辞 N2::f
应该通过限定名称查找规则进行查找,如果没有干预,它不应该找到 friend
声明f
在 N2
范围内的声明,未使用限定名称。
然而,defect report 1477 似乎有意使这种超出命名空间的定义格式正确。
在开放CWG issue 1900中重新提出了这个问题,问题描述也得出了标准的规范性文本不允许定义的结论。它还指出,正如您所观察到的那样,存在实施差异。
对于 Clang,有一个关于类似案例的错误报告 here。
考虑以下程序
#include <iostream>
namespace N1
{
inline namespace N2
{
class A
{
public:
friend void f( const A & );
private:
int x = 10;
};
//void f( const A & );
}
void N2::f( const A &a ) { std::cout << a.x << '\n'; }
}
int main()
{
using namespace N1;
A a;
f( a );
}
根据 C++ 17 标准(10.3.1.2 命名空间成员定义)
3 If a friend declaration in a non-local class first declares a class, function, class template or function template97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (6.4.1) or qualified lookup (6.4.3).
所以友元函数的名称在命名空间N2中是不可见的。所以它在命名空间 N1 中也应该是不可见的。
但是,clang HEAD 10.0.0.
编译和执行代码时没有警告编译器gcc HEAD 10.0.0 20191发出警告
prog.cc:18:10: warning: 'void N1::N2::f(const N1::N2::A&)' has not been declared within 'N1::N2'
18 | void N2::f( const A &a ) { std::cout << a.x << '\n'; }
| ^~
prog.cc:10:25: note: only here as a 'friend'
10 | friend void f( const A & );
| ^
但运行程序并输出了正确的结果。
Visual C++ 2019 也成功编译代码,没有警告,程序输出预期结果。
是不是三个编译器的bug,因为名字f
是不可见的,所以封闭命名空间中友元函数f
的定义不正确?
正如我在评论中提到的,f
的函数体和参数并不真正相关,因为问题仅与 N2::f
的名称查找有关。删除它们后,命名空间 N2
是否为 inline
也无关紧要。在这两种情况下,所有编译器的行为方式都相同。
GCC 发出警告,但在 N2::f
的定义中给出 -pedantic-errors
硬错误。 MSVC 和 Clang 始终接受未经诊断的代码。
我认为你是对的,函数定义声明符中的措辞 N2::f
应该通过限定名称查找规则进行查找,如果没有干预,它不应该找到 friend
声明f
在 N2
范围内的声明,未使用限定名称。
然而,defect report 1477 似乎有意使这种超出命名空间的定义格式正确。
在开放CWG issue 1900中重新提出了这个问题,问题描述也得出了标准的规范性文本不允许定义的结论。它还指出,正如您所观察到的那样,存在实施差异。
对于 Clang,有一个关于类似案例的错误报告 here。