在与模板专业化交朋友时可能存在gcc错误

时间:2012-11-22 23:57:49

标签: c++ class templates

在回答关于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>);
...

其最后一行给出了着名的警告

  

朋友声明'void operator*(A<T>, A<T>)'声明了一个   非模板功能

以后导致硬错误。完整代码可以找到here

现在,问题是我不认为这种行为是恰当的。 [temp.friend] / 1中的标准说:

  

对于非模板声明的友元函数声明:

     

- 如果朋友的名字是限定或不合格的模板ID,则朋友声明引用功能模板的特化,否则

     

- 如果朋友的名字是qualified-id,并且在指定的地方找到匹配的nontemplate函数   类或命名空间,友元声明引用该函数,否则,

     

- 如果朋友的名称是qualified-id并且找到了匹配的模板功能专长   在指定的类或命名空间中,friend声明引用该函数特化,否则,

这是C ++ 03; C ++ 11包含类似的子句


模板的特化由[temp.spec] / 4:

定义
  

...专门化是一个类,函数或类成员   实例化或明确专门化(14.7.3)。

和[temp.fct.spec] / 1:

  

从函数模板实例化的函数称为函数模板特化;一个人也是   功能模板的显式特化。模板参数可以明确指定...

[temp.arg.explicit] / 2说明了为函数规范指定模板参数列表:

  

当引用功能模板的特化时,可以指定模板参数列表

     

...

     

- 在朋友声明中。

     

可以推导出的尾随模板参数(14.8.2)可以从显式模板参数列表中省略。如果可以推导出所有模板参数,则可以省略它们;在这种情况下,   空模板参数列表&lt;&gt;本身也可以省略

因此,通过[temp.fct.spec] / 1,::operator*<T,T>(A<T>, A<T>)是一个函数模板特化;由于可以推导出模板参数,因此可以将其称为::operator*(A<T>, A<T>)。所以我总结了友元声明中的qualified-id表示一个函数模板特化。


我认为强调的条件已经实现;因此,友元声明应该与运营商模板(隐式)专业化的类成为朋友。但是,gcc认为不然,继续讨论第四个子弹,它只涉及由不合格的ids指定的朋友,即使这个朋友实际上是由一个合格的身份命名的。

在这种情况下,我的解释是正确的还是正确的?

1 个答案:

答案 0 :(得分:0)

我相信gcc是正确的。

首先是目前的措辞:

  

如果朋友的名字是qualified-id和匹配的函数   模板在指定的类或命名空间中找到,朋友   声明是指该函数的推导特化   模板(14.8.2.6),否则

从[14.8.2.6从函数声明中推导出模板参数]:

  

1 在声明中,声明者id指的是a的特化   函数模板,执行模板参数推导   确定声明所涉及的专业化。   具体来说,这是为了显式实例化(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才能发生(如果我正确地阅读此内容)。

<强>更新 让我们分解声明者身份对于这种情况:

qualified-id:
nested-name-specifier templateopt unqualified-id
:: identifier
:: operator-function-id
:: literal-operator-id
:: template-id

从上面的语法中可以看出,void ::operator*(A<T>, A<T>):: operator-function-id不是 :: template-id。这意味着语法永远不能声明模板函数(如错误消息中所述)。要使其成为模板ID,您必须使用operator-function-id < template-argument-listopt>语法。