5

constexpr functions can yield constexpr results even when their arguments are not constexpr, if they are unused. Such is, for example, std::integral_constant::operator T:

#include <utility>

std::integral_constant<int, 42> a;
constexpr int b = a; // Compiles!

How can I make a concept to test this property of a function?

For example, I want the following to work: run on gcc.godbolt.org

#include <utility>

struct A {explicit constexpr operator bool() const {return true;}};
struct B {int x = 1; explicit constexpr operator bool() const {return x;}};

template <typename T>
concept C = requires(T t){std::bool_constant<t>{};};

static_assert(C<A>);
static_assert(!C<B>);

But this gives me:

<source>:7:46: error: constraint variable 't' cannot be used in an evaluated context
    7 | concept C = requires(T t){std::bool_constant<t>};
      |                                              ^
0

1 Answer 1

5

After some experimenting, I ended up with this:

template <typename T>
extern T declvar;

This must be non-reference. The function call result can't be constexpr if the non-constexpr argument is a reference (at least at the time of writing).

Clang warns that the variable is not defined, but still works correctly, so the warning must be silenced with a pragma. I've reported a bug: https://github.com/llvm/llvm-project/issues/128657

In my case the full code is: run on gcc.godbolt.org

#include <utility>

struct A {explicit constexpr operator bool() const {return true;}};
struct B {int x = 1; explicit constexpr operator bool() const {return x;}};

template <typename T>
extern T declvar;

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundefined-var-template"
#endif
template <typename T>
concept C = requires{typename std::bool_constant<declvar<T> ? true : false>;};
#ifdef __clang__
#pragma clang diagnostic pop
#endif

static_assert(C<A>);
static_assert(!C<B>);
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, that's a lot of notions I missed about constexpr (member) functions. Could you provides me additional details about: why the implicit this argument is legit, while it's not a literal type, if I'm right (AFAIK, constexpr function arguments must be literal types). What is the correct reference about the fact that the non-constexpr argument must not be a reference and be unused, in order for the function being usable inside a constant expression? IMHO, the question and it's answer would be much more useful with a bit more context.
@Oersted I think the first paragraph of the question is good enough as a layman explanation. If you're looking for something more official, it's somewhere in eel.is/c++draft/expr.const#def:expression,core_constant (everything is constexpr if not explicitly disallowed there, and apparently passing unused non-constexpr arguments isn't disallowed).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.