Reputation: 21189
Please consider a C++20 concept program:
struct A {};
template<typename T>
concept DerivedOnceFromA = requires(T t) { { static_cast<const A&>(t) }; };
template<DerivedOnceFromA T>
struct B {};
struct C : A
{
B<C> foo();
};
Here the concept DerivedOnceFromA
checks that T
can be statically cast to A
. B
is template struct following this concept. And struct C
is derived from A
(so the concept is satisfied) and defines a function that returns B<C>
.
This code is accepted by GCC and MSVC, but rejected by Clang with the error:
constraints not satisfied for class template 'B' [with T = C]
Demo: https://gcc.godbolt.org/z/7Tc7xdbeq
Is it legal to use class as concepted template parameter inside class body (so what compiler is right)?
Upvotes: 4
Views: 616
Reputation: 275385
In the body of a class, that class's type is incomplete.
So you cannot have a member signature that relies on the type being complete.
In order for your code to work, you need to defer the checking of the concept until after the class is complete. One way is via a template method:
template<std::same_as<C> Y=C>
B<Y> foo()
{
return {};
}
you can even implement the only valid specialization of Y=C
within a cpp file.
This is a bit of a stupid workaround, and it does block virtual methods.
Upvotes: 1
Reputation: 170064
Legal? Yes, it's in scope. But you can't glean much of anything useful about it.
[class.mem.general]
6 A complete-class context of a class is a
- function body ([dcl.fct.def.general]),
- default argument,
- noexcept-specifier ([except.spec]), or
- default member initializer within the member-specification of the class.
7 A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing
}
of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.
So you are using it at a point it's still considered incomplete, just a type you forward declared at most. Consequentially you can't know if it's castable to some other reference, no matter the mechanism enabling the cast (inheritance or user defined conversion operator). Nor is it possible to know if an incomplete type is derived from A
even with the help of a standard concept like std::derived_from
. An incomplete type's observable properties are very limited.
Upvotes: 4