Is the following ill-formed program? If so, what the issue with giving diagnostic here?
template <typename T> struct A { int a{sizeof(T)}; };
int main() { sizeof(A<void>); }
Clang generates the following specialization for that code (clang++ -std=c++20 -Xclang -ast-print -fsyntax-only main.cpp):
template <typename T> struct A {
int a {sizeof(T)};
};
template<> struct A<void> {
int a;
};
int main() {
sizeof(A<void>);
}
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)
}
-Xclang -ast-print -fsyntax-onlytemplate<> 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With