Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class template instantiation with incomplete types

Tags:

c++

templates

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>);
}
like image 743
Dmitry Avatar asked Dec 30 '25 15:12

Dmitry


1 Answers

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.

like image 55
Jan Schultke Avatar answered Jan 02 '26 04:01

Jan Schultke