使用 'template' 关键字前缀显式访问转换函数模板是否合法?

Is it legal to prefix explicit access to a conversion function template with the 'template' keyword?

考虑以下程序:

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:

A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template or an alias template. [ Note: The keyword template may not be applied to non-template members of class templates.  — end note ] [ Note: As is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or . is not dependent on a template-parameter, or the use does not appear in the scope of a template.  — end note ]

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

The keyword template is said to appear at the top level in a qualified-id if it appears outside of a template-argument-list or decltype-specifier. [...] an optional keyword template appearing at the top level is ignored. [...]

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

我没有在 [class.conv.fct] or [temp.deduct.conv] 中找到任何与此论点冲突的条款。

问题


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

GCC接受程序错误:转换函数模板名(conversion-function-id)不是template-id

这是 CWG Defect Report 96, which has, as of GCC 10, has not yet been addressed. The related bug GCC ticket 55588 提到它正在为 GCC 11 实现。


如对 Is it possible to call templated user-defined conversion operator with explicit template arguments?, a conversion function template name does not name a template-id; quoting the grammar from template-id from [temp.names]/1 的回答所述:

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一个模板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。