另一个问题是“谁在g ++和clang ++之间做对了?”对于C ++标准大师。
以下代码
template <int>
struct foo
{
template <typename>
friend void bar ()
{ }
};
int main ()
{
foo<0> f0;
foo<1> f1;
}
使用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
用g ++编译。
像往常一样,问题是:谁是对的? g ++或clang ++?在我的Debian平台上使用clang ++ 3.9.1和g ++ 6.3.0进行检查。但是,尝试使用Wandbox,似乎与更新的版本相同。
答案 0 :(得分:5)
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)
- 结束示例]
与朋友相关的部分由DR2174添加到本段并在C ++ 17中发布(这是一个缺陷报告,因此编译器也应该将它应用于以前的标准版本。)
严格模式下的MSVC和EDG的最新版本也拒绝代码,抱怨重新定义。
[temp.inject]/1有点相关,但它只涉及朋友功能,而不是朋友功能模板:
可以在类模板中声明好友类或函数。 实例化模板时,会处理其朋友的名称 好像专业化已经明确宣布了 实例