3

考虑以下程序:

struct A {
    template <typename T>
    operator T() { return T{}; }
};

int main() {
    (void) A{}.operator int();          // (A)
    (void) A{}.template operator int(); // (B)
}

(A) 被 GCC 和 Clang 接受,而 (B) 仅被 GCC 接受但被 Clang 拒绝,并显示以下错误消息:

error: expected template name after 'template' keyword in nested name specifier

(void) A{}.template operator int(); // (B)
                    ^~~~~~~~~~~~

Afaict,(B)应该是合法的,根据[temp.names]/5

以关键字为前缀的名称template 应为模板 ID,或者名称应指类模板或别名模板。[ <em>注意:关键字template可能不适用于类模板的非模板成员。— <em>end note ] [ <em>注意:与typename前缀的情况一样,在不是严格必要的情况下允许使用template前缀;即,当嵌套名称说明符或or左侧的表达式不依赖于模板参数,或者使用未出现在模板范围内时。— <em>结束注释]->.

并且由于[temp.names]/4管辖的禁令不适用:

如果关键字出现在template-argument-listdecltype-specifier之外,则称该关键字出现在qualified-idtemplate的顶层。[...]出现在顶层的可选关键字被忽略。[...] template

并且,最多只声明关键字应该被忽略(而不是程序格式错误)。

我在[class.conv.fct][temp.deduct.conv]中没有发现任何与此论点相冲突的子句。

问题

  • template使用关键字作为对转换函数模板的显式访问前缀是否合法?

我已经针对各种语言标准版本使用各种GCCClang版本测试并重复了上述编译器的行为,但是对于这个问题的范围,我们可能会关注-std=c++17.

4

1 回答 1

3

GCC错误接受程序:一个转换函数模板名(conversion-function-id) is not a template-id

这是CWG 缺陷报告 96,截至 GCC 10,尚未解决。相关的错误 GCC 票55588提到它正在为 GCC 11 实现。


正如对是否可以使用显式模板参数调用模板化用户定义转换运算符的答案中所涵盖的那样,转换函数模板名称不命名模板ID;引用来自[temp.names]/1的template-id的语法:

simple-template-id:
  template-name < template-argument-list_opt>

template-id:
  simple-template-id
  operator-function-id < template-argument-list_opt>
  literal-operator-id < template-argument-list_opt>

template-name:
  identifier

因此,正如 OP 中所引用的那样,根据 [temp.names]/5,该template关键字不能用于作为转换函数模板名称的前缀,因为后者不是template-id

有趣的是,Clang 在引用operator-function-id(这一个template-id)时不拒绝使用关键字:

struct A {
    template <typename T>
    T operator+() { return T{}; }
};

int main() {
    (void) A{}.operator+<int>();          // (A)
    (void) A{}.template operator+<int>(); // (B)
}

如上所述,GCC 接受 OP 的程序是一个错误,因为它违反了 [temp.names]/5。

于 2020-07-23T00:50:19.167 回答