依赖模板类型的解析错误

303*_*303 3 c++ language-lawyer c++20

下面的代码符合C++20标准吗?或者,应该由于某些语法错误而被拒绝吗?所有 3 个编译器(Clang、GCC 和 MSVC)似乎都拒绝它。

template<typename...>
struct n {
    template<typename>
    struct b {
        template<typename>
        struct p {};
    };
    template<typename T>
    struct d : b<T>::template p<T> {};
    template<typename T>
    d(b<int>::template p<T>) -> d<T>;
    template<typename T>
    static constexpr auto v = d{b<int>::template p<T>{}};
};
inline constexpr auto w = n{}.v<int>;
Run Code Online (Sandbox Code Playgroud)

当删除 type 的模板头时n,所有编译器都会接受该代码。为什么这很重要?标准对基于是否模板化的代码处理方式有何规定n

演示


Clang 的错误消息:

<source>:11:15: error: 'template' keyword not permitted here
   11 |     d(b<int>::template p<T>) -> d<T>;
      |               ^~~~~~~~
<source>:11:24: error: member 'p' declared as a template
   10 |     template<typename T>
      |     ~~~~~~~~~~~~~~~~~~~~
   11 |     d(b<int>::template p<T>) -> d<T>;
      |                        ^
<source>:11:29: error: expected ';' at end of declaration list
   11 |     d(b<int>::template p<T>) -> d<T>;
      |                             ^
      |                             ;
<source>:13:54: error: expected '}'
   13 |     static constexpr auto v = d{b<int>::template p<T>{}};
      |                                                      ^
<source>:13:32: note: to match this '{'
   13 |     static constexpr auto v = d{b<int>::template p<T>{}};
      |                                ^
<source>:13:27: error: declaration of variable 'v' with deduced type 
'const auto' requires an initializer
   13 |     static constexpr auto v = d{b<int>::template p<T>{}};
      |                           ^
<source>:13:27: error: declaration of variable 'v' with deduced type 
'const auto' requires an initializer
<source>:15:31: note: in instantiation of static data member 'n<>::v<int>'
requested here
   15 | inline constexpr auto w = n{}.v<int>;
      |                               ^
<source>:15:23: error: constexpr variable 'w' must be initialized by a
constant expression
   15 | inline constexpr auto w = n{}.v<int>;
      |                       ^   ~~~~~~~~~~
<source>:15:27: note: non-literal type 'auto' cannot be used in a constant
expression
   15 | inline constexpr auto w = n{}.v<int>;
      |                           ^
Run Code Online (Sandbox Code Playgroud)

GCC的错误信息:

<source>:13:32: error: missing template arguments before '{' token
   13 |     static constexpr auto v = d{b<int>::template p<T>{}};
      |                                ^
<source>:15:31: error: 'struct n<>' has no member named 'v'
   15 | inline constexpr auto w = n{}.v<int>;
      |                               ^
<source>:15:33: error: expected primary-expression before 'int'
   15 | inline constexpr auto w = n{}.v<int>;
      |                                 ^~~
Run Code Online (Sandbox Code Playgroud)

MSVC的错误信息:

<source>(13): error C2760: syntax error: '{' was unexpected here; expected '}'
<source>(13): note: the template instantiation context (the oldest one first) is
<source>(14): note: see reference to class template instantiation 
'n<<unnamed-symbol>...>' being compiled
Run Code Online (Sandbox Code Playgroud)

正如HolyBlackCat提到的,添加typename到使得在GCC和MSVC上b<int>::template p<T>{}的构建成为可能。d然而,Clang 仍然拒绝该代码,因为它似乎与推导指南存在问题。

这给我留下了一个问题:是否 typename可以从d的推导指南中省略,同时仍然符合 C++20 标准。MSVC 还允许省略typename何时将大括号更改为括号,但我认为这种行为是不合规的。

Hol*_*Cat 5

没有必要这样做,有一个简单的解释。

b<int>是一个依赖类型,它依赖于 的模板参数n

您可能会问,它是如何依赖的?因为它可以专门化,例如:

template <>
template <>
struct n<int>::b<long> {};
Run Code Online (Sandbox Code Playgroud)

如果您在 前面加上typenameb<int>则代码可以工作......但 Clang 不喜欢推导指南,这可能应该是一个单独的问题。

<source>:11:5: error: deduction guide template contains a template parameter that cannot be deduced
   11 |     d(typename b<int>::template p<T>) -> d<T>;
      |     ^
Run Code Online (Sandbox Code Playgroud)