Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing error with dependent template type

Is the following code in accordance with the C++20 standard? Or, should it be rejected due to some syntax error? All 3 compilers (Clang, GCC, and MSVC) seem to reject it.

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>;

When removing the template-head of type n, all compilers accept the code. Why does this matter? What does the standard have to say about the way the code should be processed based on whether n is templated?

Demo


Clang's error message:

<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>;
      |                           ^

GCC's error message:

<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>;
      |                                 ^~~

MSVC's error message:

<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

As mentioned by HolyBlackCat, adding typename to b<int>::template p<T>{} makes the construction of d possible on GCC and MSVC. Clang still rejects the code because it seems to have an issue with the deduction guide, however.

This leaves me with the question whether typename can be omitted from d's deduction guide while still being compliant to the C++20 standard. MSVC also allows the omittance of typename when changing the curley braces to parentheses, but I presume this behavior to be non-compliant.

like image 587
303 Avatar asked Jun 28 '26 23:06

303


1 Answers

I don't think language-lawyering this is necessary, there's a simple explanation.

b<int> is a dependent type, it depends on the template parameters of n.

How is it dependent, you may ask? Because it can be specialized, e.g.:

template <>
template <>
struct n<int>::b<long> {};

If you prepend typename to b<int>, the code works... except Clang doesn't like the deduction guide, which perhaps should be a separate question.

<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>;
      |     ^
like image 101
HolyBlackCat Avatar answered Jun 30 '26 13:06

HolyBlackCat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!