Consider the following concept, which relies on the operator bool()
conversion member function of std::is_lvalue_reference<T>
and std::is_const<T>
.
#include <type_traits>
template <typename T>
concept is_non_const_lvalue_reference =
std::is_lvalue_reference<T>{} && // Malformed for GCC 12.2 and MSVC 19.33
!std::is_const<std::remove_reference_t<T>>{};
static_assert(is_non_const_lvalue_reference<int&>);
static_assert(!is_non_const_lvalue_reference<int const&>);
static_assert(!is_non_const_lvalue_reference<int&&>);
See the test at https://compiler-explorer.com/z/G5ffeWfbx.
Both GCC and MSVC correctly report that the returned type of std::is_lvalue_reference<T>{}
is not a Boolean value. But Clang 15 then applies the implicit conversion to bool
and considers the concept definition above well-formed. In contrast, the !std::is_const<...>{}
expression is considered a Boolean expression due to the !
operator.
The question: Is Clang 15 being too permissive in performing the implicit conversion to bool
? Or is it a problem with GCC and MSVC? Perhaps it's unspecified in C 20?
P.S. Yes, I know I can replace the std::is_*<T>{}
expressions with std::is_*_v<T>
, and thus avoid the issue. But I'd like to understand when implicit conversions are expected/allowed to be performed.
CodePudding user response:
The rule is, from [temp.constr.atomic]/3:
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 is performed if necessary, and
E
shall be a constant expression of typebool
.
Here, std::is_lvalue_reference<T>{}
is an atomic constraint - so it must have type bool
. It doesn't.