g ++和clang ++在模板类中定义的朋友模板函数的不同行为

max*_*x66 9 c++ templates friend language-lawyer

另一个问题是"g ++和clang ++之间谁是对的?" 对于C++标准大师.

以下代码

template <int>
struct foo
 {
   template <typename>
   friend void bar ()
    { }
 };

int main ()
 {    
   foo<0>  f0;
   foo<1>  f1;
 }
Run Code Online (Sandbox Code Playgroud)

使用clang ++编译没有问题(只有两个"未使用的变量"警告)但是给出以下错误

tmp_002-11,14,gcc,clang.cpp: In instantiation of ‘struct foo<1>’:
tmp_002-11,14,gcc,clang.cpp:27:12:   required from here
tmp_002-11,14,gcc,clang.cpp:20:16: error: redefinition of ‘template<class> void bar()’
    friend void bar ()
                ^~~
tmp_002-11,14,gcc,clang.cpp:20:16: note: ‘template<class> void bar()’ previously defined here
Run Code Online (Sandbox Code Playgroud)

用g ++编译.

像往常一样,问题是:谁是对的?g ++或clang ++?

在我的Debian平台上查看了clang ++ 3.9.1和g ++ 6.3.0.但是,尝试使用Wandbox,似乎与更新的版本相同.

bog*_*dan 6

GCC在这种情况下是对的.

相关的标准措辞在[temp.inst]/2中:

类模板特化的隐式实例化导致
- 未删除的类成员函数,成员类,作用域成员枚举,静态数据成员,成员模板和朋友的声明的隐式实例化,而不是定义的隐式实例化; 和
[...]
但是,用于确定根据6.2和12.2,对应于在模板的定义被认为是一个定义的声明的实例化的重声明是否有效的目的.[ 例如: [...]

template<typename T> struct Friendly {
   template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // ill-formed: produces second definition of f(U)
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

与朋友相关的部分由DR2174添加到本段并在C++ 17中发布(这是一个缺陷报告,因此编译器也应该将它应用于以前的标准版本).


最新版本的MSVC和EDG在严格模式下也拒绝代码,抱怨重新定义.

[temp.inject]/1有点相关,但它只谈到朋友的功能,而不是朋友的功能模板:

可以在类模板中声明好友类或函数.实例化模板时,会将其朋友的名称视为在实例化时明确声明了特化.