For class templates, the definitions inside will only be instantiated when used.
In the example:
template <typename T>
struct A {
int a{sizeof(T)};
};
An application of sizeof
to A<void>
only requires an instantiation of A
and knowing what types the data members have. The expression sizeof(T)
is type-dependent on T
and not necessary, so neither GCC nor Clang will instantiate it here. This is allowed:
The implicit instantiation of a class template specialization causes
- the implicit instantiation of [...]
The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions.
- [temp.inst]/3
Initializers of non-static data members are not listed in this paragraph, so we are safe so far. The member will actually be instantiated when:
[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist or if the existence of the definition of the member affects the semantics of the program;
- [temp.inst]/4
The definition of a
and its initializers don't have to be instantiated for sizeof(A<void>)
, because it is not required to exist and doesn't affect the semantics of the program.
However, if you created a variable with type A<void>
, then the compiler would need to instantiate the default member initializer in A
, making the program ill-formed:
int main() {
sizeof(A<void>); // OK up to this point
A<void> a; // ill-formed (GCC issues warning, Clang issues error)
}
Note on -Xclang -ast-print -fsyntax-only
template<> struct A<void> {
int a;
};
Note that what you are seeing is only a representation of what clang has instantiated so far. The generated syntax tree doesn't contain the default member initializer of a
at all. This is not a representation of what A<void>
is once fully instantiated (including all members), only what what clang knows so far.