This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 114a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-04-28


2739. Nested requirement not a constant expression

Section: 7.5.7.5  [expr.prim.req.nested]     Status: open     Submitter: Barry Revzin     Date: 2022-09-20

Consider:

  struct NonConstant { static bool f() { return true; } };
  struct True { static constexpr bool f() { return true; } };
  struct False { static constexpr bool f() { return false; } };

  template <class T>
  concept C = requires { requires T::f(); };

  static_assert(!C<NonConstant>);   // #1
  static_assert(C<True>);
  static_assert(!C<False>);

clang accepts; gcc, MSVC, and EDG all consider #1 to be ill-formed. Subclause 7.5.7.5 [expr.prim.req.nested] paragraph 1 specifies:

The constraint-expression shall be satisfied (13.5.3 [temp.constr.decl]) by the substituted template arguments, if any. Substitution of template arguments into a nested-requirement does not result in substitution into the constraint-expression other than as specified in 13.5.2 [temp.constr.constr].

Neither C<NonConstant> nor C<False> are saisfied, but it is unclear whether those two cases should be treated differently. Subclause 13.5.2.3 [temp.constr.atomic] paragraph 3 might be relevant:

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. Otherwise, the lvalue-to-rvalue conversion (7.3.2 [conv.lval]) is performed if necessary, and E shall be a constant expression of type bool.

An example from the standard library is:

  template<class G>
   concept uniform_random_bit_generator =
     invocable<G&> && unsigned_integral<invoke_result_t<G&>> &&
     requires {
       { G::min() } -> same_as<invoke_result_t<G&>>;
       { G::max() } -> same_as<invoke_result_t<G&>>;
       requires bool_constant<(G::min() < G::max())>::value;
    };

Is the bool_constant part necessary to ensure that G::min() < G::max() is a constant expression?