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?
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;
};