admin管理员组

文章数量:1221846

I made a macro for the "implies" operator. It works fine in general, but breaks on Clang when used in a concept or a requires-clause.

run on gcc.godbolt

namespace detail
{
    template <typename T>
    concept BoolLike = requires(T &&t) {(T &&)t ? true : false;};

    struct Implies
    {
        template <BoolLike T>
        friend constexpr bool operator||(T &&lhs, Implies)
        {
            return !bool((T &&)lhs);
        }
    };
}

#define IMPLIES ||::detail::Implies{}||

template <typename T>
concept A = true IMPLIES true;

static_assert(A<int>);

Clang says:

<source>:19:18: error: atomic constraint must be of type 'bool' (found '::detail::Implies')
   19 | concept A = true IMPLIES true;
      |                  ^~~~~~~
<source>:16:19: note: expanded from macro 'IMPLIES'
   16 | #define IMPLIES ||::detail::Implies{}||
      |                   ^~~~~~~~~~~~~~~~~~~

While GCC and MSVC happily accept this code. Which compiler is correct?

I made a macro for the "implies" operator. It works fine in general, but breaks on Clang when used in a concept or a requires-clause.

run on gcc.godbolt.org

namespace detail
{
    template <typename T>
    concept BoolLike = requires(T &&t) {(T &&)t ? true : false;};

    struct Implies
    {
        template <BoolLike T>
        friend constexpr bool operator||(T &&lhs, Implies)
        {
            return !bool((T &&)lhs);
        }
    };
}

#define IMPLIES ||::detail::Implies{}||

template <typename T>
concept A = true IMPLIES true;

static_assert(A<int>);

Clang says:

<source>:19:18: error: atomic constraint must be of type 'bool' (found '::detail::Implies')
   19 | concept A = true IMPLIES true;
      |                  ^~~~~~~
<source>:16:19: note: expanded from macro 'IMPLIES'
   16 | #define IMPLIES ||::detail::Implies{}||
      |                   ^~~~~~~~~~~~~~~~~~~

While GCC and MSVC happily accept this code. Which compiler is correct?

Share Improve this question asked Feb 6 at 19:22 HolyBlackCatHolyBlackCat 96.3k13 gold badges167 silver badges268 bronze badges 2
  • concept A = static_cast<bool>(true IMPLIES true); works though Demo – Jarod42 Commented Feb 7 at 8:45
  • @Jarod42 Yep. But it's a shame I can't get the rhs to be its own atomic constraint with manually spelling this as !a || b. – HolyBlackCat Commented Feb 7 at 9:25
Add a comment  | 

1 Answer 1

Reset to default 6

Overloaded logical operators don't really work in constraint expressions (at the top level): the constraint normalization machinery turns expressions of the forms E1 || E2 and E1 && E2 into disjunctions and conjunctions, respectively, based purely on their syntactic form, without regard to whether there is a user-provided overload for the operator ([temp.constr.normal]).

In this particular example, this means that the normal form of A's constraint-expression contains, among others, an atomic constraint formed from the expression ::detail::Implies{}, which is never of type bool.

[temp.constr.atomic]/3:

To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. [...] E shall be a constant expression of type bool.

Since this program never checks the invalid atomic constraint for satisfaction (because || short-circuits), it is ill-formed with no diagnostic required ([temp.res.general]/6.4):

The program is ill-formed, no diagnostic required, if

  • any constraint-expression in the program, introduced or otherwise, has (in its normal form) an atomic constraint A where no satisfaction check of A could be well-formed and no satisfaction check of A is performed

本文标签: cOverloaded ampamp operators in concepts and requiresclausesStack Overflow