The following code compiles with MSVC but fails with GCC and Clang for missing typename
before a dependent type:
struct Foo { struct value{ }; };
struct Bar { int value; };
template<typename T>
constexpr size_t SIZE = sizeof(T::value); // missing typename here, for Foo
constexpr size_t s1 = SIZE<Foo>; // missing typename before dependent type above
constexpr size_t s2 = SIZE<Bar>;
MSVC approach, not requiring typename for sizeof
, seems reasonable, as sizeof works on both types and variables. On the other hand GCC and Clang seem to play by the book, as this is one of those cases where you still need typename
even in C++20, when the context cannot reveal to the compiler whether it is going to meet a type or a variable.
The question is whether MSVC is allowed to be permissive here, that is, if the compiler can perform the required operation correctly without the typename
, is it allowed to do so? Or does it contradict the specification?
The difference between the approach of MSVC vs. Clang and GCC comes to an actual difference in the following code, which compiles by all three compilers but behaves differently:
template<typename T> concept A1 = sizeof(typename T::value) > 0;
template<typename T> concept A2 = sizeof(T::value) > 0;
struct Foo { struct value{ }; };
constexpr bool v1 = A1<Foo>; // true with all
constexpr bool v2 = A2<Foo>; // true with MSVC, false with GCC and Clang
In the above code, GCC and Clang see the omission of typename
in A2 as illegal and thus fail the concept, this is not compilation error but rather having the concept getting the boolean value false ([temp.constr.atomic]).
C++20 spec ([temp.res.general]) lists the places where typename
is not required for assuming a qualified-id is a type. The sizeof
operator is not in that list, so it seems that it should require typename
for a template dependent-type. On the other hand, sizeof
doesn't appear in the example as ill-formed, no diagnostic required for missing typename
(not being in the example does not say anything, but still keeps the question, in a way).
Why sizeof
may allow inferring proper operation without adding typename
? Mainly because it shouldn't really care if this is a type or a variable. And the spec does not say specifically that this is ill-formed. If it does say that it is ill-formed, then a pointer to this should be the answer, which will probably make MSVC behavior a defect.
As a side note, that does not answer the question, one can make all three compilers happy with the following code:
template<typename T>
constexpr size_t SIZE = sizeof(T::value);
template<typename T> requires requires { typename T::value; }
constexpr size_t SIZE<T> = sizeof(typename T::value);
And for the concept:
template<typename T> concept A =
sizeof(T::value) > 0 ||
sizeof(typename T::value) > 0;