Reputation: 157344
Suppose I'm writing a class template some of whose members are constrained on the presence and value of a type template parameter static constexpr data member:
template<class T>
struct A {
constexpr bool operator()() requires T::value { return T::value; }
constexpr bool operator()() { return false; }
};
#include <type_traits>
static_assert(A<std::true_type>()());
static_assert(!A<std::false_type>()());
static_assert(!A<void>()());
MSVC and gcc accept this, but clang rejects unless I replace requires T::value
with requires requires { requires T::value; }
. Is this a bug in clang, or are the other compilers lax; is it the case that C++ requires requires requires requires? What does the Standard say?
Related (well, ⅔ at least): Why do we require requires requires?
Upvotes: 6
Views: 493
Reputation: 302932
This is a clang bug (submitted #49513).
It looks like clang is substituting void
into T::value
and deciding that because that's an invalid expression, that the constraint is invalid. But the rule, in [temp.constr.atomic] is that:
To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied.
In this case, substitution results in an invalid type or expression, so the result of that should be that the constraint is not satisfied.
Note that this overload:
constexpr bool operator()() requires T::value { return T::value; }
is only valid if T::value
is a valid expression and evaluates to true
. Which makes it equivalent to this:
constexpr bool operator()() requires T::value { return true; }
Which is fine in this case, since you're returning false
in the other case anyway, so there's no reason to distinguish T::value
exists but is false
from T::value
does not exist.
But thought it was worth clarifying anyway.
Upvotes: 4