How to check that a C++ class is incomplete (only declared)? [duplicate]
Asked Answered
J

3

4

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?

Jehad answered 29/7, 2021 at 9:50 Comment(3)
You can easily have ODR violations / inconsistent result with is_complete/is_incomplete. EOF is a valid point of instantiation. For function, definition should be identical for each instantiation, for class, it is instantiated only once. Not sure for template variable.Dialectics
but I think most answers there suffer from same/similar problem as yoursNovanovaculite
this might be the only correct one: https://mcmap.net/q/25289/-using-sfinae-to-check-if-the-type-is-complete-or-not-duplicateNovanovaculite
M
6

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).

Mcevoy answered 29/7, 2021 at 10:46 Comment(0)
J
1

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.

Jehad answered 30/7, 2021 at 7:0 Comment(1)
That applies to template functions. For classes, there is only one instantiation... :-/Dialectics
O
1

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.)

Obit answered 11/5, 2023 at 9:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.