This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
std::basic_string_view
comparison operators are overspecifiedSection: 27.3.2 [string.view.synop] Status: WP Submitter: Giuseppe D'Angelo Opened: 2023-06-21 Last modified: 2024-04-02
Priority: Not Prioritized
View all issues with WP status.
Discussion:
The <string_view>
synopsis in 27.3.2 [string.view.synop] has these signatures
for operator==
and operator<=>
:
// 27.3.4 [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 27.3.4 [string.view.comparison], sufficient additional overloads of comparison functions
In 27.3.4 [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).
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<=>
).
type_identity_t
) is indeed necessary to
implement the "sufficient additional overloads" part of 27.3.4 [string.view.comparison],
but it is also sufficient, as all the following cases
sv == 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 regardingoperator<=>
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.
type_identity_t
.
[Kona 2023-11-10; move to Ready]
Editorial issue 6324 provides the changes as a pull request to the draft.
[Tokyo 2024-03-23; Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4950.
Modify 27.3.2 [string.view.synop], header <string_view>
synopsis, as indicated:
[…] // 27.3.4 [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 27.3.4 [string.view.comparison], sufficient additional overloads of comparison functions[…]
Modify 27.3.4 [string.view.comparison] as indicated:
-1- LetS
bebasic_string_view<charT, traits>
, andsv
be an instance ofS
. Implementations shall provide sufficient additional overloads markedconstexpr
andnoexcept
so that an objectt
with an implicit conversion toS
can be compared according to Table 81 [tab:string.view.comparison.overloads].
Table 81: Additionalbasic_string_view
comparison overloads [tab:string.view.comparison.overloads]ExpressionEquivalent tot == sv
S(t) == sv
sv == t
sv == S(t)
t != sv
S(t) != sv
sv != t
sv != S(t)
t < sv
S(t) < sv
sv < t
sv < S(t)
t > sv
S(t) > sv
sv > t
sv > S(t)
t <= sv
S(t) <= sv
sv <= t
sv <= S(t)
t >= sv
S(t) >= sv
sv >= t
sv >= S(t)
t <=> sv
S(t) <=> sv
sv <=> t
sv <=> S(t)
[Example 1: A sample conforming implementation foroperator==
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
-4- Mandates:R
denote the typetraits::comparison_category
if that qualified-id is valid and denotes a type (13.10.3 [temp.deduct]), otherwiseR
isweak_ordering
.R
denotes a comparison category type (17.11.2 [cmp.categories]). -5- Returns:static_cast<R>(lhs.compare(rhs) <=> 0)
. [Note: The usage oftype_identity_t
as parameter ensures that an object of typebasic_string_view<charT, traits>
can always be compared with an object of a typeT
with an implicit conversion tobasic_string_view<charT, traits>
, and vice versa, as per 12.2.2.3 [over.match.oper]. — end note]