This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 3347. `std::pair<T, U>` now requires `T` and `U` to be less-than-comparable

Section: 22.3.3 [pairs.spec], 24.3.7.1 [array.overview] Status: C++20 Submitter: Jonathan Wakely Opened: 2019-12-03 Last modified: 2021-02-25

Priority: 1

View all other issues in [pairs.spec].

View all issues with C++20 status.

Discussion:

P1614R2 added `operator<=>` as a hidden friend to `std::pair`:

>     friend constexpr common_comparison_category_t<synth-three-way-result<T1>,
>                                                   synth-three-way-result<T2>>
>       operator<=>(const pair& x, const pair& y) { see below }

That is not a function template, so is not a SFINAE context. If one or both of `synth-three-way-result<T1>` or `synth-three-way-result<T2>` is an invalid type then the declaration of `operator<=>` is ill-formed, and so the specialization `std::pair<T1, T2>` is ill-formed.

A similar problem exists for `std::array`.

There are at least two ways to fix this:

1. Constrain the function and delay the use of synth-three-way-result until we know it's valid.

2. Replace the hidden friend with a namespace-scope function template, so invalid `synth-three-way-result` types cause substitution failure.

The first option is somewhat hard to specify, because current policy is to avoid the use of requires-clauses in most of the library clauses. Even with a requires-clause, the potentially-invalid `synth-three-way-result` types cannot be used in the function declarator. Furthermore, the `operator<=>` for `std::array` is currently specified in Table [tab:container.opt] and so there's nowhere to add a Constraints: element.

The second option would partially revert the P1614R2 changes for `std::pair` and `std::array` and bring them closer to what was in C++17. The main motivation for making `operator==` a hidden friend was to allow it to be defaulted, so that `std::pair` and `std::array` would be usable as non-type template parameters. Following the acceptance of P1907 in Belfast it isn't necessary to default it, so we can go back to what was in C++17.

[2019-12-12 Issue Prioritization]

Priority to 1 after reflector discussion.

[2020-02-10 Move to Immediate Monday afternoon in Prague]

Proposed resolution:

This wording is relative to N4842.

1. Modify 22.2.1 [utility.syn] as indicated:

   > [Drafting note: This restores the pre-P1614R2 `operator==` and uses `operator<=>` as replacement for `operator<`, `operator<=`, `operator>`, `operator>=`.]

   >     […]

   >     // 22.3 [pairs], class template pair
   >     template<class T1, class T2>
   >     struct pair;

   >     // 22.3.3 [pairs.spec], pair specialized algorithms
   >     template<class T1, class T2>
   >       constexpr bool operator==(const pair<T1, T2>&, const pair<T1, T2>&);
   >     template<class T1, class T2>
   >       constexpr common_comparison_category_t<synth-three-way-result<T1>,
   >                                              synth-three-way-result<T2>>
   >       operator<=>(const pair<T1, T2>&, const pair<T1, T2>&);

   >     template<class T1, class T2>
   >     constexpr void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));
   >     […]

2. Modify 22.3.2 [pairs.pair] as indicated:

   >       […]
   >       constexpr void swap(pair& p) noexcept(see below);

   >       // 22.3.3 [pairs.spec], pair specialized algorithms
   >       friend constexpr bool operator==(const pair&, const pair&) = default;
   >       friend constexpr bool operator==(const pair& x, const pair& y)
   >         requires (is_reference_v<T1> || is_reference_v<T2>)
   >         { return x.first == y.first && x.second == y.second; }
   >       friend constexpr common_comparison_category_t<synth-three-way-result<T1>,
   >                                                     synth-three-way-result<T2>>
   >         operator<=>(const pair& x, const pair& y) { see below }
   >     };

   >     template<class T1, class T2>
   >     pair(T1, T2) -> pair<T1, T2>;
   >     […]

3. Modify 22.3.3 [pairs.spec] as indicated:

   > 20.4.3 Specialized algorithms [pairs.spec]

   >     template<class T1, class T2>
   >       constexpr bool operator==(const pair<T1, T2>& x, const pair<T1, T2>& y);

   > > -?- Returns: `x.first == y.first && x.second == y.second`.

   >     template<class T1, class T2>
   >     friend constexpr
   >       common_comparison_category_t<synth-three-way-result<T1>, synth-three-way-result<T2>>
   >         operator<=>(const pair<T1, T2>& x, const pair<T1, T2>& y);

   > > -1- Effects: Equivalent to:

   > > >     if (auto c = synth-three-way(x.first, y.first); c != 0) return c;
   > > >     return synth-three-way(x.second, y.second);

4. Modify 24.3.2 [array.syn] as indicated:

   > [Drafting note: This restores the pre-P1614R2 `operator==` and uses `operator<=>` as replacement for `operator<`, `operator<=`, `operator>`, `operator>=`.]

   >     namespace std {

   >     // 24.3.7 [array], class template array
   >     template<class T, size_t N> struct array;

   >     template<class T, size_t N>
   >       constexpr bool operator==(const array<T, N>& x, const array<T, N>& y);
   >     template<class T, size_t N>
   >       constexpr synth-three-way-result<T>
   >         operator<=>(const array<T, N>& x, const array<T, N>& y);

   >     template<class T, size_t N>
   >     constexpr void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));
   >     […]

5. Modify 24.3.7.1 [array.overview] as indicated:

   > [Drafting note: there is no need to add definitions of `operator==` and `operator<=>` to [array.spec] because they are defined by Table 71: Container requirements [tab:container.req] and Table 73: Optional container operations [tab:container.opt] respectively.]

   >       […]
   >       constexpr T * data() noexcept;
   >       constexpr const T * data() const noexcept;

   >       friend constexpr bool operator==(const array&, const array&) = default;
   >       friend constexpr synth-three-way-result<value_type>
   >         operator<=>(const array&, const array&);
   >     };

   >     template<class T, class... U>
   >     array(T, U...) -> array<T, 1 + sizeof...(U)>;
   >     […]