Guillaume Racicot
Guillaume Racicot

Reputation: 41770

Checking for constexpr in a concept

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

Answers (2)

Alex Vaskov
Alex Vaskov

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

Fedor
Fedor

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

Related Questions