Created on 2023-06-21.00:00:00 last changed 8 months ago
Proposed resolution:
This wording is relative to N4950.
Modify [string.view.synop], header <string_view> synopsis, as indicated:
[…] // [string.view.comparison], non-member comparison functions template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> x, type_identity_t<basic_string_view<charT, traits>> y) noexcept; template<class charT, class traits> constexpr see below operator<=>(basic_string_view<charT, traits> x, type_identity_t<basic_string_view<charT, traits>> y) noexcept;// see [string.view.comparison], sufficient additional overloads of comparison functions[…]
Modify [string.view.comparison] as indicated:
-1- Let S be basic_string_view<charT, traits>, and sv be an instance of S. Implementations shall provide sufficient additional overloads marked constexpr and noexcept so that an object t with an implicit conversion to S can be compared according to Table 81 [tab:string.view.comparison.overloads].
Table 81: Additional basic_string_view comparison overloads [tab:string.view.comparison.overloads]ExpressionEquivalent tot == svS(t) == svsv == tsv == S(t)t != svS(t) != svsv != tsv != S(t)t < svS(t) < svsv < tsv < S(t)t > svS(t) > svsv > tsv > S(t)t <= svS(t) <= svsv <= tsv <= S(t)t >= svS(t) >= svsv >= tsv >= S(t)t <=> svS(t) <=> svsv <=> tsv <=> S(t)
[Example 1: A sample conforming implementation for operator== would be:template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs) noexcept { return lhs.compare(rhs) == 0; } template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, type_identity_t<basic_string_view<charT, traits>> rhs) noexcept { return lhs.compare(rhs) == 0; }
— end example]template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, type_identity_t<basic_string_view<charT, traits>> rhs) noexcept;-2- Returns: lhs.compare(rhs) == 0.
template<class charT, class traits> constexpr see below operator<=>(basic_string_view<charT, traits> lhs, type_identity_t<basic_string_view<charT, traits>> rhs) noexcept;-3- Let R denote the type traits::comparison_category if that qualified-id is valid and denotes a type ([temp.deduct]), otherwise R is weak_ordering.
-4- Mandates: R denotes a comparison category type ([cmp.categories]). -5- Returns: static_cast<R>(lhs.compare(rhs) <=> 0). [Note: The usage of type_identity_t as parameter ensures that an object of type basic_string_view<charT, traits> can always be compared with an object of a type T with an implicit conversion to basic_string_view<charT, traits>, and vice versa, as per [over.match.oper]. — end note]
[ Tokyo 2024-03-23; Status changed: Voting → WP. ]
[ Kona 2023-11-10; move to Ready ]
Editorial issue 6324 provides the changes as a pull request to the draft.
The <string_view> synopsis in [string.view.synop] has these signatures for operator== and operator<=>:
// [string.view.comparison], non-member comparison functions template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) noexcept; template<class charT, class traits> constexpr see below operator<=>(basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) noexcept; // see [string.view.comparison], sufficient additional overloads of comparison functions
In [string.view.comparison], paragraph 1 states that "Implementations shall provide sufficient additional overloads" so that all comparisons between a basic_string_view<C, T> object and an object of a type convertible to basic_string_view<C, T> work (with the reasonable semantics).
The associated Example 1 proposes this implementation strategy for operator==:template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs) noexcept { return lhs.compare(rhs) == 0; } template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, type_identity_t<basic_string_view<charT, traits>> rhs) noexcept { return lhs.compare(rhs) == 0; }
With the current semantics of rewritten candidates for the comparison operators, it is however superfluous to actually specify both overloads (the same applies for operator<=>).
The second overload (using type_identity_t) is indeed necessary to implement the "sufficient additional overloads" part of [string.view.comparison], but it is also sufficient, as all the following casessv == sv
sv == convertible_to_sv
convertible_to_sv == sv
can in fact use it (directly, or after being rewritten e.g. with the arguments swapped).
The reason why we still do have both operators seems to be historical; there is an explanation offered here by Barry Revzin. Basically, there were three overloads before a bunch of papers regarding operator<=> and operator== were merged:operator==(bsv, bsv) to deal with sv == sv;
operator==(bsv, type_identity_t<bsv>) and
operator==(type_identity_t<bsv>, bsv) to deal with sv == convertible_to_sv and vice versa.
Overload (1) was necessary because with only (2) and (3) a call like sv == sv would otherwise be ambiguous. With the adoption of the rewriting rules, overload (3) has been dropped, without realizing that overload (1) would then become redundant.
The specification of these overloads can be greatly simplified by adjusting the signatures to explicitly use type_identity_t.History | |||
---|---|---|---|
Date | User | Action | Args |
2024-04-02 10:29:12 | admin | set | messages: + msg14032 |
2024-04-02 10:29:12 | admin | set | status: voting -> wp |
2024-03-18 09:32:04 | admin | set | status: ready -> voting |
2023-11-10 19:15:47 | admin | set | messages: + msg13831 |
2023-11-10 19:15:47 | admin | set | status: new -> ready |
2023-06-25 10:47:28 | admin | set | messages: + msg13666 |
2023-06-21 00:00:00 | admin | create |