tuple
relational operators have confused friendshipsSection: 22.4.9 [tuple.rel] Status: New Submitter: Corentin Jabot Opened: 2023-02-08 Last modified: 2023-03-22
Priority: 3
View other active issues in [tuple.rel].
View all other issues in [tuple.rel].
View all issues with New status.
Discussion:
In 22.4.9 [tuple.rel]:
template<class... TTypes, tuple-like UTuple> constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);
Is defined as a non-member non-friend function that "is to be found via argument-dependent lookup only."
The intent is that it should be defined as a hidden friend intuple
.
The current specification is confusing as to which class should contain that hidden friend, or how to otherwise
implement that adl only restriction. (An hostile reading may consider it to be a hidden friend of UTuple
),
and does not follow the guidance of P1601 "Recommendations for Specifying ``Hidden Friends''".
We should consider making these operator==
and operator<=>
overloads hidden friends of
tuple
, i.e.
std::tuple { template<class... TTypes, tuple-like UTuple> friend constexpr bool operator==(const tuple& t, const UTuple& u); template<class... TTypes, tuple-like UTuple> friend constexpr see below operator<=>(const tuple&, const UTuple&); };
[2023-02-18; Daniel provides wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
Modify 22.4.2 [tuple.syn], header
<tuple>
synopsis, as indicated:namespace std { […] // 22.4.9 [tuple.rel], relational operators template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);[…] }Modify 22.4.4 [tuple.tuple], class template
tuple
synopsis, as indicated:namespace std { template<class... Types> class tuple { public: […] template<tuple-like UTuple> constexpr tuple& operator=(UTuple&&); template<tuple-like UTuple> constexpr const tuple& operator=(UTuple&&) const; // 22.4.9 [tuple.rel], relational operators template<tuple-like UTuple> friend constexpr bool operator==(const tuple&, const UTuple&); template<tuple-like UTuple> friend constexpr see below operator<=>(const tuple&, const UTuple&); // 22.4.4.4 [tuple.swap], tuple swap constexpr void swap(tuple&) noexcept(see below); constexpr void swap(const tuple&) const noexcept(see below); }; }Modify 22.4.9 [tuple.rel] as indicated:
template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);-1- For the first overload let
[…] -5- Remarks:UTuple
betuple<UTypes...>
. For the second overload letTTypes
denote the packTypes
.
(5.1) — The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates to
false
.(5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.
template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...> operator<=>(const tuple<TTypes...>& t, const UTuple& u);-6- For the second overload, let
[…] -8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.TTypes
denote the packTypes
andElems
denotes the pack of typestuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>
.
[2023-03-05; Daniel comments and provides improved wording]
The revised wording ensures that no ambiguity exists between the overload candidates, furthermore the additional wording about being found via argument-dependent lookup only has been eliminated, because the general conventions of 16.4.6.6 [hidden.friends] apply.
The reusage of the exposition-only conceptdifferent-from
is already existing practice in various places of
22.4 [tuple]. We have also existing wording practice where we have an extra Constraints: element added even though
a prototype has already a language constraint as part of its signature.
[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4928.
Modify 22.4.2 [tuple.syn], header <tuple>
synopsis, as indicated:
namespace std { […] // 22.4.9 [tuple.rel], relational operators template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);[…] }
Modify 22.4.4 [tuple.tuple], class template tuple
synopsis, as indicated:
namespace std { template<class... Types> class tuple { public: […] template<tuple-like UTuple> constexpr tuple& operator=(UTuple&&); template<tuple-like UTuple> constexpr const tuple& operator=(UTuple&&) const; // 22.4.9 [tuple.rel], relational operators template<tuple-like UTuple> friend constexpr bool operator==(const tuple&, const UTuple&); template<tuple-like UTuple> friend constexpr see below operator<=>(const tuple&, const UTuple&); // 22.4.4.4 [tuple.swap], tuple swap constexpr void swap(tuple&) noexcept(see below); constexpr void swap(const tuple&) const noexcept(see below); }; }
Modify 22.4.9 [tuple.rel] as indicated:
template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);-1- For the first overload let
-?- Constraints: For the second overload,UTuple
betuple<UTypes...>
. For the second overload letTTypes
denote the packTypes
.different-from<UTuple, tuple>
(25.5.2 [range.utility.helpers]) istrue
. -2- Mandates: […] […] -5- Remarks:
(5.1) —The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates tofalse
.
(5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...> operator<=>(const tuple<TTypes...>& t, const UTuple& u);-6- For the second overload, let
-?- Constraints: For the second overload,TTypes
denote the packTypes
andElems
denotes the pack of typestuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>
.different-from<UTuple, tuple>
(25.5.2 [range.utility.helpers]) istrue
. -7- Effects: […]-8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.