Reputation: 41770
I just started doing concepts. The syntax for checking expressions is really useful and removes me a lot of boilerplate I had with sfinae. However I wondered how can I check if an expression can be used in a constexpr context. Sometimes those expression results in void. The way I can imagine would look like this, with the constexpr keyword uncommented:
template<typename T>
concept foo = requires(T t) {
/* constexpr */ { t.bar(); } -> std::same_as<void>;
/* constepxr */ { T::foo; };
}
However, I highly doubt this is the right syntax. Is there an easy way to check for the constexpr-ness of the expression in a concept?
I don't want to check if the evaluation of a constexpr function won't fail for all possible values of t
, I want to know if I can put that expression at a place where the compiler expect something to be evaluable at compile time.
Upvotes: 13
Views: 1932
Reputation: 248
How about this one:
template <typename F, auto Test=std::bool_constant<(F{}(), true)>()>
consteval auto is_constexpr (F) { return Test; }
EDIT: the one I wrote before doesn't work in clang > 13.0 for some reason, but this does
and then
requires (T t) {
is_constexpr([]{ T{}.cols(); });
}
Here we use the C++20's consteval func to force constexpr check inside and the fact that since C++20 simple lambdas can be default-constructed, hence we construct it here from F{} and here we go :)
Upvotes: 2
Reputation: 21189
I think the expected concept can be created using std::bool_constant
, which has the property that the failure of substitution in its argument of not-constant expression is not a compilation error, but just makes the concept false
.
The proposed solution is
#include <concepts>
#include <type_traits>
template<typename T>
concept HasConstexprVoidBar =
requires(T t) {
{ t.bar() } -> std::same_as<void>;
{ std::bool_constant<(T{}.bar(), true)>() } -> std::same_as<std::true_type>;
};
struct A {
constexpr void bar() {}
};
struct B {
void bar() {}
};
int main() {
// concept check passes for constexpt A::bar()
static_assert( HasConstexprVoidBar<A> );
// concept check fails for B::bar()
static_assert( !HasConstexprVoidBar<B> );
}
Here the concept HasConstexprVoidBar
is verified successfully for struct A
having constexpr void
method bar
and evaluates to false
for struct B
having not-constexpr
method.
Demo: https://gcc.godbolt.org/z/nsx9z99G4
Upvotes: 7