5

在回答关于 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>)”声明了一个非模板函数

导致以后出现硬错误。完整的代码可以在这里找到。

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

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

— 如果朋友的名字是一个合格的或不合格的模板ID,朋友声明是指一个函数模板的特化,否则

— 如果朋友的名字是一个qualified-id,并且在指定的类或命名空间中找到了一个匹配的非模板函数,则朋友声明引用该函数,否则,

— 如果朋友的名字是一个qualified-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>)。所以我得出结论,朋友声明中的限定 ID 表示函数模板特化。


我认为强调的条件得到满足;因此,friend 声明应该与具有操作符模板(隐式)特化的类友好。然而,gcc 不这么认为并继续第四个项目符号,它只涉及由 unqualified-ids 指定的朋友,即使该朋友实际上是由一个qualified-id 命名的

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

4

1 回答 1

0

我相信 gcc 是正确的。

首先是当前的措辞:

如果朋友的名字是一个qualified-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 是声明中的函数类型或与 5.3.4 中描述的放置运算符 new 匹配的释放函数的类型。扣除按照 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

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

于 2012-11-24T00:16:04.737 回答