Home > Back-end >  Inlined requires-expression for SFINAE, and using as constexpr bool
Inlined requires-expression for SFINAE, and using as constexpr bool

Time:04-23

TL;DR: My question is that requires {...} can be used as a constexpr bool expression by the standard?

I haven't found anything about that in the standard, but it simplifies a lot and results a much cleaner code. For example in SFINAE instead of enable_if, or some ugly typename = decltype(declval<>()...), or something else, it is a simple clean requires-expression.

This is my example:

#include <type_traits>

struct foo { typedef int type; };

struct bar { ~bar() = delete; };

/**
 * get_type trait, if T::type is valid, get_type<T>::type
 * equal to T::type, else void
 */
// T::type is valid
template<typename T, bool = requires{typename T::type;}>
struct get_type : std::type_identity<typename T::type> {};

// T::type is invalid
template<typename T>
struct get_type<T, false> : std::type_identity<void> {};

/// Template alias, this is buggy on GCC 11.1 -> internal compiler error
template<typename T>
using get_type_t = typename get_type<T>::type;

// Tests
static_assert(std::is_same_v<get_type_t<foo>, int>);
static_assert(std::is_same_v<get_type_t<bar>, void>);


/**
 * Destructible trait
 *
 * In libstdc  -v3 this is the implementation for the testing
 * 
 * struct __do_is_destructible_impl
 * {
 *   template <typename _Tp, typename = decltype(declval<_Tp &>().~_Tp())>
 *   static true_type __test(int);
 *
 *   template <typename>
 *   static false_type __test(...);
 * };
 */

// This is the same: 
template<typename T>
struct my_destructible_impl : std::bool_constant< requires(T t) { t.~T(); } >
{};

// Tests
static_assert(my_destructible_impl<foo>::value);
static_assert(!my_destructible_impl<bar>::value);

I found that it will evaluate to true or false if I'm correct:

The substitution of template arguments into a requires-expression used in a declaration of a templated entity may result in the formation of invalid types or expressions in its requirements, or the violation of semantic constraints of those requirements. In such cases, the requires-expression evaluates to false and does not cause the program to be ill-formed. The substitution and semantic constraint checking proceeds in lexical order and stops when a condition that determines the result of the requires-expression is encountered. If substitution (if any) and semantic constraint checking succeed, the requires-expression evaluates to true.

So I would like to ask if requires {...} can be safely used as a constexpr bool expression as in my example, or not? Because based on cppreference.com I'm not 100% sure, but I feel like it is and it compiles with clang and GCC. However in the standard I haven't found anything about that (or maybe I just can't use ctrl f properly...). And I haven't found anything where someone use the requires-expression like this...

CodePudding user response:

requires {...} is a requires-expression and according to expr.prim.req/p2 it is a prvalue:

A requires-expression is a prvalue of type bool whose value is described below. Expressions appearing within a requirement-body are unevaluated operands.

So yes, you can use it in a constexpr bool context.

  • Related