Reputation: 21307
I would like to write a C++ function that will check that its template parameter class is incomplete, so only class declaration is available but not full definition with all class members.
My function incomplete()
looks as follows together with some demo program:
#include <type_traits>
#include <iostream>
template <typename T, typename V = void> constexpr bool is_incomplete = true;
template <typename T> constexpr bool is_incomplete<T, std::enable_if_t<sizeof(T)>> = false;
template <typename T> constexpr bool incomplete() { return is_incomplete<T>; }
struct A;
void print() { std::cout << incomplete<A>(); }
struct A {}; //this line affects GCC
int main()
{
print();
}
It works well in Clang printing 1
, but in GCC the program prints 0
despite the fact that A
class is incomplete in function print
.
https://gcc.godbolt.org/z/qWW3hqbEv
Is GCC wrong here or there is a fault in my program?
Upvotes: 5
Views: 879
Reputation: 20533
It has already been correctly stated that this can cause ODR violations and thus, undefined behavior. To make this issue more concrete, consider the program below whose runtime behavior depends on whether a static_assert
is present or not! Obviously this is really nasty.
// is_incomplete is defined in the OP.
struct foo;
template <typename T>
void f() {
if (is_incomplete<T>)
puts("hello");
else
puts("goodbye");
}
int main() {
f<foo>();
}
static_assert(is_incomplete<foo>);
struct foo{};
When compiled with GCC 13.1 and Clang 16.0 the program displays hello
. However, if the static_assert
is removed, then the output is goodbye
.
(See here.)
Upvotes: 1
Reputation: 21307
It looks like the finding whether a class is incomplete will be a violation of one definition rule (ODR) if at some point it becomes complete, so there should be no valid solution to the question.
Some more quotes from https://eel.is/c++draft/temp.point that I was suggested:
1 For a function template specialization ... the point of instantiation ... immediately follows the namespace scope declaration or definition that refers to the specialization.
7 A specialization for a function template ... may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above,
for any such specialization that has a point of instantiation within the ... the translation-unit ..., the point after the ... [end of] the translation-unit is also considered a point of instantiation,
If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.
Upvotes: 1
Reputation: 170173
Which compiler is correct is currently undecided. It's CWG Issue 1845.
The current wording of 13.8.4.1 [temp.point] does not define the point of instantiation of a variable template specialization. Presumably replacing the references to “static data member of a class template” with “variable template” in paragraphs 1 and 8 would be sufficient.
Given that issue is still in drafting stage (and no normative wording exists yet it the latest available draft), it remains unresolved. It isn't clear if a variable template can have multiple points of instantiation or not (though the direction the issue reporter suggests is clear).
For entities with multiple points of instantiation, the end of the translation unit is one as well. Also, if two points disagree on the definition of template, it's an ODR violation, plain and simple. GCC seems to provide two points, so you see the result of that. And I tend to agree with GCC here. A variable template is in many ways a shorthand for a static data member of a class template, and static data members do have multiple points of instantiation.
Either way, this is playing with the risk of nasal demons. Checking a type for complete-ness is not really possible reliably if that state can change in a TU (and possibly even if it can change in the entire program).
Upvotes: 6