Considering the following code example, I would expect to have to use the template
keyword here to guide the compiler to treat variable v
as a template. However, MSVC rejects the use of the template
keyword, whereas Clang and GCC actually require it. Which specific rule in the C++20 standard either mandates or forbids the use of the template
keyword in this case?
struct s {
template<typename...>
static constexpr auto v = true;
};
// all ok
static_assert([](auto x){ return decltype(x)::template v<>; }(s{}));
// clang ok, gcc ok, msvc nope
static_assert([](auto x){ return x.template v<>; }(s{}));
// clang nope, gcc nope, msvc ok
static_assert([](auto x){ return x.v<>; }(s{}));
The error message from Clang:
<source>:10:36: error: use 'template' keyword to treat 'v'
as a dependent template name
10 | static_assert([](auto x){ return x.v<>; }(s{}));
| ^
| template
The error message from MSVC:
<source>(8): error C2187: syntax error: 'template' was unexpected here
<source>(8): note: see reference to function template instantiation
'auto <lambda_2>::operator ()<s>(_T1) const' being compiled
with
[
_T1=s
]
In short, x
and decltype(x)
are dependent, and template
is allowed and necessary in all cases. There is an MSVC compiler bug.
As for dependent types, including decltype
:
A type is dependent if it is:
- a template parameter,
- [...]
- denoted by
decltype(expression)
, whereexpression
is type-dependent.
Furthermore, x
is type-dependent because (see [temp.dep.expr] p3.1) it is:
associated by name lookup with one or more declarations declared with a dependent type
The auto
parameter for the generic lambda makes the call operator a function template, so naturally, x
is associated with a template parameter, which is dependent.
Therefore template
is necessary for decltype(x)::template v<>
. All compilers agree and this is correct.
In the statement
return x.template v<>;
x
is type-dependent because it is associated by name lookup with an implicit template parameter of the generic lambda's call operator. Therefore, template
is necessary.
Note: Besides the standardese, it's intuitively necessary because x.v <
could be interpreted as "x.v
is less than ..." if it isn't known that v
is a template.
This is formally stated in [temp.names] p3.
See also: Where and why do I have to put the "template" and "typename" keywords?
The fact that MSVC doesn't allow .template v<>
is obviously a bug. It's worth noting that MSVC does accept:
[](s x){ return x.template v<>; }(s{})
However, using auto
instead of s
as the parameter type leads to a compiler error. This makes no sense at all, since template
and typename
are optional and adding them unnecessarily should never cause failure.
Note: I was unable to find an existing bug report in the Microsoft Developer Community, so maybe this hasn't been reported yet. You may want to report this bug.