Kyle Knoepfel
Kyle Knoepfel

Reputation: 1698

Implicit conversion in concepts

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.

Upvotes: 3

Views: 131

Answers (1)

Barry
Barry

Reputation: 302748

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 type bool.

Here, std::is_lvalue_reference<T>{} is an atomic constraint - so it must have type bool. It doesn't.

Upvotes: 3

Related Questions