jpa*_*cek 5 c++ templates class
在回答关于SO的不同问题时,我遇到了一个与gcc有点可疑的编译器错误.有问题的片段是
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>);
...
Run Code Online (Sandbox Code Playgroud)
最后一行给出了着名的警告
friend declament'
void operator*(A<T>, A<T>)'声明一个非模板函数
导致以后的硬错误.完整的代码可以在这里找到.
现在,问题是我不认为这种行为是恰当的.[temp.friend]/1中的标准说:
对于不是模板声明的友元函数声明:
- 如果朋友的名称是限定或不合格的模板ID,则友元声明引用功能模板的特化,否则
- 如果friend的名称是qualified-id并且在指定的类或命名空间中找到匹配的nontemplate函数,则friend声明引用该函数,否则,
- 如果friend的名称是qualified-id并且 在指定的类或命名空间中找到了模板函数的匹配特化,则friend声明引用该函数的特化,否则,
这是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>).所以我总结了friend声明中的qualified-id表示一个函数模板特化.
我认为强调的条件已经实现; 因此,友元声明应该与运营商模板(隐式)专业化的类成为朋友.然而,gcc认为不然,并继续第四个子弹,只涉及由不合格的id指定的朋友,即使这个朋友实际上是由一个合格的id命名.
在这种情况下,我的解释是正确的还是gcc?
我相信gcc是正确的。
首先是目前的措辞:
如果友元的名称是限定 ID,并且在指定的类或命名空间中找到匹配的函数模板,则友元声明引用该函数模板的推导特化(14.8.2.6),否则
来自[14.8.2.6 从函数声明推导模板参数]:
1在其 declarator-id 引用函数模板的特化的声明中,将执行模板实参推导来标识该声明所引用的特化。 具体来说,这是针对显式实例化 (14.7.2)、显式特化 (14.7.3) 和某些友元声明 (14.5.4) 完成的。这样做还可以确定释放函数模板特化是否与放置运算符 new 匹配(3.7.4.2、5.3.4)。在所有这些情况下,P 是被视为潜在匹配的函数模板的类型,A 是声明中的函数类型或与放置运算符 new 匹配的释放函数的类型,如 5.3.4 中所述。扣除按 14.8.2.5 中所述进行。
2 如果对于这样考虑的一组函数模板,在考虑部分排序后没有匹配或有多个匹配(14.5.6.2),则推导失败,并且在声明情况下,程序格式错误。
在您的情况下,不会执行模板参数推导,因为 declarator-id 不引用专门化。我认为重要的是whose declarator-id refers to a specialization作为这一切发生的条件。简而言之,您需要使<>第一句话14.8.2.6p1发生(如果我没有正确阅读的话)。
更新 让我们分解一下这种情况下的 declarator-id 是什么:
qualified-id:
nested-name-specifier templateopt unqualified-id
:: identifier
:: operator-function-id
:: literal-operator-id
:: template-id
Run Code Online (Sandbox Code Playgroud)
从上面的语法可以看出,void ::operator*(A<T>, A<T>)is a:: operator-function-id而不是 a :: template-id。这意味着语法永远不能声明模板函数(如错误消息中所述)。要使其成为模板 ID,您必须使用operator-function-id < template-argument-listopt>语法。