3

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.

1
  • Here is a list of contexts where you can omit typename. Looks like the deduction guide parameters aren't there. Commented Feb 13, 2024 at 12:02

1 Answer 1

5

I don't think ing 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>;
      |     ^
Sign up to request clarification or add additional context in comments.

3 Comments

MSVC seems to accept the construction of d with p without use of typename when changing {} to (). Is that behavior non-compliant with the C++20 standard? Which specific rule mandates this?
Why is typename necessary for p when constructing d but not when specifying p for d's deduction guide? Which specific rule in the C++20 standard describes the difference in use of typename here?
@303 here you can find lots of details, I think also links to relevant parts of the standard stackoverflow.com/q/610245/4117728

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.