【发布时间】:2012-11-11 08:58:12
【问题描述】:
在回答关于 SO 的另一个问题时,我遇到了一个有点可疑的 gcc 编译器错误。有问题的sn-p是
template <class T> class A;
template <class T, class U>
void operator*(A<T>, A<U>);
template <class T>
class A {
friend void ::operator*(A<T>, A<T>);
...
最后一行给出了著名的警告
朋友声明'
void operator*(A<T>, A<T>)'声明一个 非模板函数
导致以后出现硬错误。完整代码可以在here找到。
现在,问题是我认为这种行为不合适。 [temp.friend]/1 中的标准说:
对于不是模板声明的友元函数声明:
——如果朋友的名字是合格或不合格的模板ID,朋友声明是指一个函数模板的特化,否则
——如果朋友的名字是一个限定ID,并且在指定的 类或命名空间,友元声明引用该函数,否则,
——如果朋友的名字是一个限定ID并且找到了一个模板函数的匹配特化 在指定的类或命名空间中,友元声明引用该函数特化,否则,
这是C++03; C++11 包含类似的子句
模板的特化由 [temp.spec]/4 定义:
... 特化是类、函数或类成员,它是 实例化或显式特化 (14.7.3)。
和 [temp.fct.spec]/1:
从函数模板实例化的函数称为函数模板特化;一个也是 函数模板的显式特化。模板参数可以显式指定...
[temp.arg.explicit]/2 说明了为函数规范指定模板参数列表:
在引用函数模板的特化时可以指定模板参数列表
...
——在朋友声明中。
可以从显式模板参数列表中省略可以推导的尾随模板参数 (14.8.2)。如果所有的模板参数都可以推导出来,那么它们都可以被省略;在这种情况下, 空模板参数列表本身也可以省略。
所以,通过 [temp.fct.spec]/1,::operator*<T,T>(A<T>, A<T>) 是一个函数模板特化;并且由于可以推导出模板参数,所以可以称为::operator*(A<T>, A<T>)。所以我得出结论,朋友声明中的qualified-id表示一个函数模板特化。
我认为强调的条件已经满足;因此,friend 声明应该与具有操作符模板(隐式)特化的类友好。然而,gcc 不这么认为,并继续讨论第四个项目符号,它只涉及由非限定 ID 指定的朋友,即使该朋友实际上是由一个合格 ID 命名的。
在这种情况下我的解释是正确的还是 gcc 正确?
【问题讨论】:
-
我的 GCC 说,“如果这不是您想要的,请确保函数模板已经被声明并在此处的函数名称后面添加
<>”,确实可以使用operator*<>. -
@KerrekSB:是的,我知道——你可能已经注意到,ideone gcc 也是这么说的;但是,问题不在于 gcc 所说的内容或存在哪些解决方法,而是 gcc 所说的是否符合标准。
-
嗯,
operator*在您看来是不是“合格 ID”?我认为该语言指的是friend void Bar<T>::zip()之类的东西。 -
@KerrekSB:
::operator*,在 sn-p 中使用,确实看起来确实像一个限定 ID。 -
MSVC 需要与msdn.microsoft.com/en-us/library/f1b2td24(v=vs.110).aspx 相同的语法,所以我怀疑这是编译器错误。据我所知,
<>语法等同于friend void ::operator*<T>(A<T>, A<T>);。希望更熟悉该标准的人可以解释原因。