6

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?

cigien
  • 57,834
  • 11
  • 73
  • 112
ecatmur
  • 152,476
  • 27
  • 293
  • 366

1 Answers1

4

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Ah yes, I may have oversimplified the example. It makes a little more sense with (a lot of) context. – ecatmur Mar 10 '21 at 18:15
  • 1
    This bug in Clang is fixed in trunk by now: https://github.com/llvm/llvm-project/issues/48857 – Fedor Oct 06 '22 at 06:28