3

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>);
}
Aamir
  • 1,974
  • 1
  • 14
  • 18
Dmitry
  • 1,065
  • 7
  • 15
  • Your code simply does nothing. As this, the compiler has no need to do anything at all. And why the code should be ill-formed? I can't see any incomplete type. – Klaus Jul 04 '23 at 11:12
  • @Klaus `sizeof(void)` is not allowed. THough I agree that its not obvious at all in the question. I suppose they refer to this https://stackoverflow.com/questions/1666224/what-is-the-size-of-void – 463035818_is_not_an_ai Jul 04 '23 at 11:14
  • 1
    @463035818_is_not_an_ai `sizeof(void)` on itself doesn't compile (as it's not allowed), but this code does. This example forces instantiation of `A` (you can see clang actually created a specialization for it). The question is why did compiler allow it to compile. What I wonder is whether it's IFNDR or fully defined behaviour. – Dmitry Jul 04 '23 at 11:17
  • @463035818_is_not_an_ai: I agree that sizeof(void) makes no sense, but it is not about incomplete types. And the final expression will always generate the result of sizeof(int), whatever the a is initialized to, because it is never used. All that code makes no sense! if we write `std::cout << A{}.a << std::endl;` gcc generates `invalid application of 'sizeof' to a void type` – Klaus Jul 04 '23 at 11:20
  • @Klaus I only use `void` here in order to avoid declaring any incomplete types. But it behaves the same if you introduce `class F;` declaration and replace `void` with `F` – Dmitry Jul 04 '23 at 11:22
  • @Klaus this seems to be a question about what is legal and what not. The code does not need to make sense. Its an example of what it is and sufficient as example for the question. Typically code examples for `language-lawyer` questions make no sense. The question is not if you would write it, but if you can – 463035818_is_not_an_ai Jul 04 '23 at 11:24
  • As said: No need to instantiate the template nor to create an object from it. The compiler simply will ignore all and everything. If you, as said in my last comment, use a value, you get the error message as expected. Maybe one of the 1000+ "no diagnostic required" thing :-) – Klaus Jul 04 '23 at 11:25
  • @Klaus the point is that the compiler **does instantiate** it, but in a kinda "interesting" way. Since it can't get size of void it just ignores it – Dmitry Jul 04 '23 at 11:26

1 Answers1

6

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.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96