13

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;
Amir Kirsh
  • 12,564
  • 41
  • 74
  • 2
    Can't answer your question directly, but MSVC always seemed to allow missing `typename`'s in template code. This goes all the way back to C++ 98. So Visual C++ has always played fast and loose with this, while g++ was more strict. – PaulMcKenzie Feb 14 '23 at 08:10
  • 1
    Interesting. `/permissive-` doesn't alter MSVCs behavior. – StoryTeller - Unslander Monica Feb 14 '23 at 08:21
  • Also, as an aside, MS doesn't seem to address the `typename` issue until the next major compiler release. I've had situations where code that compiles fine with VS 2015 will not compile under VS 2017, 2019, etc. all because of the missing `typename` that should have been detected in VS 2015. So if I were you, I would hedge my bet and put the `typename` in, as g++ suggests (it won't hurt). – PaulMcKenzie Feb 14 '23 at 08:43
  • 1
    Note the last three words in the paragraph you linked from the on-line draft Standard: "no diagnostic required". – Adrian Mole Feb 14 '23 at 09:55
  • 1
    @AdrianMole what is also curious, that sentences in that paragraph are separated by full stops. – Language Lawyer Feb 14 '23 at 10:56

1 Answers1

1

The standard is very indirect about this point, but it’s pretty clear that MSVC is not conforming here. (In the non-SFINAE/concept case, it would be allowed to issue merely a warning (“operand of sizeof considered a type despite missing ‘typename’”) and continue, but that’s not really the point.)

Note that even in the trivial case of

struct X;
int y=X;

the error is that X can’t be interpreted as an unqualified-id because it is not “suitably declared” ([expr.prim.id.unqual]/1). In a template the grammatical interpretation of a dependent name is fixed by the presence or absence of typename, despite the fact that qualified-ids are thereby produced without knowing whether their terminal names are so suitably declared; we evidently must reject them if eventually they are found wanting in that regard.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76