I'd like to check how and if an object of type A can be constructed from an object of type B.
I also want to distinguished between direct and copy initialization and thus wrote these concepts:
template<typename A, typename B>
concept can_direct_init_from = requires { A{std::declval<B>()}; };
template<typename A, typename B>
concept can_copy_init_from = requires { A a = std::declval<B>(); };
The first one, I think, is strictly equivalent to std::constructible_from
but I don't know of a similar concept for the copy initialization case.
std::convertible_to
does not seem to be used for a copy initialization (according to the corresponding traits, it is equivalent to testing a static_cast
from B
to A
).
Besides my version is ill-formed:
error: expected primary-expression before 'a'
AFAU, I can not "declare" variable inside a `requires clause.
Is there a way to test for "is copy initializable from"? Possibly by fixing my concept?
I'd like to check how and if an object of type A can be constructed from an object of type B.
I also want to distinguished between direct and copy initialization and thus wrote these concepts:
template<typename A, typename B>
concept can_direct_init_from = requires { A{std::declval<B>()}; };
template<typename A, typename B>
concept can_copy_init_from = requires { A a = std::declval<B>(); };
The first one, I think, is strictly equivalent to std::constructible_from
but I don't know of a similar concept for the copy initialization case.
std::convertible_to
does not seem to be used for a copy initialization (according to the corresponding traits, it is equivalent to testing a static_cast
from B
to A
).
Besides my version is ill-formed:
error: expected primary-expression before 'a'
AFAU, I can not "declare" variable inside a `requires clause.
Is there a way to test for "is copy initializable from"? Possibly by fixing my concept?
Share Improve this question asked Feb 6 at 14:15 OerstedOersted 2,5946 silver badges25 bronze badges 2 |2 Answers
Reset to default 5Sounds like std::is_convertible_v<B, A>
, a.k.a. std::convertible_to<B, A>
.
The way that's traditionally implemented is to put the conversion in a function argument:
template<class T> T produce();
template<class T> void consume(T);
template<class A, class B>
concept can_copy_init_from =
requires { consume<A>(produce<B>()); };
There are traits in <type_traits>
to handle it; you have:
std::is_constructible<T, args...>
for explicit constructor(conversion) calls. It accepts all conversion operators(explicit or not) for single argument initialization.std::is_convertible<from,to>
for implicit conversions. Every eligible single-argument constructor(including constructors with default arguments) is a converting constructor(= conversion), and accepted by the trait.
For single argument direct initialization, all conversion constructors and conversion operators are considered.
For single argument aggregate/copy initialization, only implicit(none-explicit) are considered.
The above traits therefore, reflect the accepted syntax. They are generally defined via none-portable compiler intrinsics. But if you need to manually definie them:
template<typename T, typename ...Targs>
concept my_is_constructible =
requires(Targs ...vargs)
{ T(static_cast<Targs>(vargs)...); };
template<typename from, to>
concept my_is_convertible =
requires(from x, void(*f)(to))
{ (*f)(static_cast<from>(x)); };
The second trait only succeeds iff implicit conversion is available. The forwarding problem has a lot of corners to consider; I am trying to avoid complexity via a self-cast through static_cast
, but it may need further consideration. Nevertheless, all such definitions implicitly cover destructability too(which must always be true anyway).
void can_copy_init_from_helper(A);
and in conceptrequires(B b) { can_copy_init_from_helper<A>(b); };
seems to do the trick: godbolt.org/z/TsWrPGfeY – Yksisarvinen Commented Feb 6 at 14:32std::convertible_to
requires both implicit conversion andstatic_cast
to be valid and expects them to have the same effect. This is usually more reasonable than just checking implicit conversion. – cpplearner Commented Feb 6 at 14:47