c++c++20c++-conceptsincomplete-type

C++20 concepts and incomplete types


I'm forward declaring a type and providing its definition later down the line. Once it has been fully defined it is expected to meet some requirements, and to be a suitable template argument. However, when pairing incomplete types with C++20 concepts I run into problems.

The following compiles without a hitch using GCC 13.2.0:

#include <concepts>

template <class T>
class class_without_constrained_template {};

struct A;
struct B;

struct B { class_without_constrained_template<A> var; };
struct A {};

int main() {
  return 0;
};

If I try to formalize the requirements using C++20 concepts compilation fails, and the compiler complains that A is not std::destructible:

#include <concepts>

template <std::destructible T>
class class_with_constrained_template {};

struct A;
struct B;

struct B { class_with_constrained_template<A> var; };
struct A {};

int main() {
  return 0;
};

I realize that at the time of defining B, A is an incomplete type that does not fulfill the requirements. However, it would fulfill them by the time you were instantiating it. I have found this related question, but the proposed solution does not seem to work when class_with_constrained_template is used as a member variable. The following fails to compile.

#include <concepts>

template <class T>
class class_with_constrained_template {
  static_assert( std::destructible<T> );
};

template <std::destructible T>
class class_with_constrained_template<T> {};

struct A;
struct B;

struct B { class_with_constrained_template<A> _; };
struct A {};

int main() {
  return 0;
};

In the above snippet, when is static_assert evaluated, and why does it evaluate to false? Is it possible to use C++20 concepts in this scenario? If so, how?


Solution

  • However, it would fulfill them by the time you were instantiating it

    No. Thats not right. B is a class. It is not a template. It is nothing that gets instantiation later. You define it and thats it. In the Q&A you link the B is a template that is instantiated later only when the needed type is complete.

    If you make B a template, the answer from the other question applies:

    #include <concepts>
    
    template <class T>
    class class_with_constrained_template {
      static_assert( std::destructible<T> );
    };
    
    template <std::destructible T>
    class class_with_constrained_template<T> {};
    
    struct A;
    
    template <typename T = A>
    struct B { class_with_constrained_template<T> _; };
    struct A {};
    
    
    int main() {
        B<> a;
    };
    

    Live Demo