std::pair<T, U>
now requires T
and U
to be less-than-comparableSection: 22.3.3 [pairs.spec], 23.3.3.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.
std::array
.
There are at least two ways to fix this:
Constrain the function and delay the use of synth-three-way-result until we know it's valid.
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.
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.
Modify 22.2.1 [utility.syn] as indicated:
[Drafting note: This restores the pre-P1614R2
operator==
and usesoperator<=>
as replacement foroperator<
,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))); […]
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>; […]
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>friendconstexpr 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);
Modify 23.3.2 [array.syn] as indicated:
[Drafting note: This restores the pre-P1614R2
operator==
and usesoperator<=>
as replacement foroperator<
,operator<=
,operator>
,operator>=
.]
namespace std { // 23.3.3 [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))); […]
Modify 23.3.3.1 [array.overview] as indicated:
[Drafting note: there is no need to add definitions of
operator==
andoperator<=>
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)>; […]