Should the compiler be able to deduce the type of the non-type template parameter of function template f that is defaulted to n{}? Which specific rule in the C++20 standard allows/mandates this?
template<typename = int>
struct s {
template<typename = int>
struct n {};
template<n = n{}>
constexpr int f();
};
static_assert(requires { s{}.f(); }); // clang ok, gcc ok, msvc nope
Surprisingly, MSVC for some reason accepts the following code while the other compilers won't even allow the syntax. Is MSVC simply having a tea party with its imaginary friends here? And, is there a workaround that works for all 3 compilers, other than changing the type of the NTTP of f from n to auto?
template<typename = int>
struct s {
template<typename = int>
struct n {};
template<n<typename> = n{}>
constexpr int f();
};
static_assert(requires { s{}.f(); }); // clang nope, gcc nope, msvc ok
Regarding the workaround, one way to go about it is to add a member-type to n and use a requires-clause to do a manual type-constraint check. This is not the type of workaround I'm looking for, it adds too much bloat and is simply too cumbersome. Also, having to add explicit constness to the types just to evade some other compiler discrepancy isn't particularly appealing to me either.
#include <concepts>
template<typename = int>
struct s {
template<typename T = int>
struct n { using type = T; };
template<auto N = n{}>
requires std::same_as<decltype(N) const,
n<typename decltype(N)::type> const>
constexpr int f();
};
The error produced by MSVC:
<source>(9): error C2672: 's<int>::f': no matching overloaded function found
<source>(6): note: could be 'int s<int>::f(void)'
<source>(9): note: 'int s<int>::f(void)': could not deduce template argument
for '__formal'
<source>(9): error C2607: static assertion failed
C++20 [temp.arg.nontype]/1 permits class template argument deduction for template parameters:
In the template-parameter
n = n{}, the twons are "placeholders for a deduced class type". The type of the template parameter, represented by the firstn, should be deduced from the initializern{}, giving it the typen<int>.This paragraph doesn't specifically address whether the template-argument can come from a default template argument, but even MSVC doesn't think this is actually disallowed: MSVC is happy to accept it when
nandfaren't nested withins:There is no reason why this should make a difference. MSVC probably just has a bug. It is not clear to me how to work around it.