Title
`std::indirect`'s `operator==` still does not support incomplete types
Status
new
Section
[indirect.relops] [indirect.comp.with.t]
Submitter
Hewill Kang

Created on 2025-08-24.00:00:00 last changed 7 days ago

Messages

Date: 2025-08-24.17:41:25

Proposed resolution:

This wording is relative to N5014.

[Drafting note:: We introduce the exposition-only function FUN below to mimic the implicit conversion to `bool`. As a drive-by effect this helps us simplifying (and clarifying, see LWG 484) the existing Mandates element.

Please note that the seemingly unresolved `T` in the `requires` expression below names the first template parameter of the `indirect` class template. ]

  1. Modify [indirect.relops] as indicated:

    template<class U, class AA>
      constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs)
        noexcept(noexcept(*lhs == *rhs)see below);
    

    -?- Let FUN denote the exposition-only function

    bool FUN(bool) noexcept;
    

    -1- Mandates: The expression FUN(*lhs == *rhs) is well-formed and its result is convertible to `bool`.

    -2- Returns: If `lhs` is valueless or `rhs` is valueless, `lhs.valueless_after_move() == rhs.valueless_after_move()`; otherwise `*lhs == *rhs`.

    -?- Remarks: The exception specification is equivalent to:

    requires (const T& lhs, const U& rhs) { { FUN(lhs == rhs) } noexcept; }
    
  2. Modify [indirect.comp.with.t] as indicated:

    template<class U>
      constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(noexcept(*lhs == rhs)see below);
    

    -?- Let FUN denote the exposition-only function

    bool FUN(bool) noexcept;
    

    -1- Mandates: The expression FUN(*lhs == rhs) is well-formed and its result is convertible to `bool`.

    -2- Returns: If `lhs` is valueless, `false`; otherwise `*lhs == rhs`.

    -?- Remarks: The exception specification is equivalent to:

    requires (const T& lhs, const U& rhs) { { FUN(lhs == rhs) } noexcept; }
    
Date: 2025-08-24.00:00:00

`std::indirect`'s `operator== intentionally uses Mandates instead of Constraints to support incomplete types. However, its function signature has the following `noexcept` specification:

template<class U, class AA>
  constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs)
    noexcept(noexcept(*lhs == *rhs));

That is, we check whether the expression `*lhs == *rhs` throws, which unfortunately leads to the following hard error:

struct Incomplete;
static_assert(std::equality_comparable<std::indirect<Incomplete>>);
// hard error, no match for 'operator==' (operand types are 'const Incomplete' and 'const Incomplete')

This makes `operator==` not SFINAE-friendly for incomplete types, which defeats the purpose.

Also, checking `noexcept(*lhs == *rhs)` seems insufficient because the result of `*lhs == *rhs` might still throw during conversion to `bool`.

History
Date User Action Args
2025-08-24 17:30:44adminsetmessages: + msg14956
2025-08-24 00:00:00admincreate