std::sub_match::compare(string_view) overloadSection: 28.6.8 [re.submatch] Status: New Submitter: Jonathan Wakely Opened: 2018-06-26 Last modified: 2024-10-03
Priority: 3
View all other issues in [re.submatch].
View all issues with New status.
Discussion:
std::sub_match::compare can be called with a basic_string or a pointer to a null-terminated
character sequence, but can't be called with a basic_string_view. To compare to a string_view
requires either conversion to basic_string (with a potential allocation) or a redundant call to
traits_type::length to calculate a length that is already known.
[2018-07-02, Jonathan comments and completes proposed wording]
To make the relational and equality operators for sub_match support string views I propose specifying
the semantics, not adding another 12 overloaded operators to namespace std, in addition to the 42
already there. This allows them to be implemented as "hidden friends" if the implementation so desires, or to
retain namespace-scope declaration if backwards compatibility with C++11 - C++17 is preferred.
[2018-07-20 Priority set to 3 after reflector discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4750.
Change 28.6.3 [re.syn], header
<regex>synopsis, as indicated:#include <initializer_list> namespace std { […] using csub_match = sub_match<const char*>; using wcsub_match = sub_match<const wchar_t*>; using ssub_match = sub_match<string::const_iterator>; using wssub_match = sub_match<wstring::const_iterator>;// 28.6.8.3 [re.submatch.op], sub_match non-member operators template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); […] template<class BiIter> bool operator>=(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs);template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m); […] }Change 28.6.8 [re.submatch], class template
sub_matchsynopsis, as indicated:namespace std { template<class BidirectionalIterator> class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> { public: using value_type = typename iterator_traits<BidirectionalIterator>::value_type; […] int compare(const sub_match& s) const; int compare(const string_type& s) const; int compare(const value_type* s) const; int compare(basic_string_view<value_type> s) const; }; }Change 28.6.8.2 [re.submatch.members] as indicated:
int compare(const value_type* s) const;-7- Returns:
str().compare(s).int compare(basic_string_view<value_type> s) const;-?- Returns:
str().compare(s).Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:
31.9.2
sub_matchnon-member operators [re.submatch.op]template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
-1- Returns:lhs.compare(rhs) == 0.[…]template<class BiIter> bool operator>=(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs);
-42- Returns:!(lhs < rhs).template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);-43- Returns:
os << m.str().Class template
sub_matchprovides overloaded relational operators (7.6.9 [expr.rel]) and equality operators (7.6.10 [expr.eq]) for comparisons with anothersub_match, with astring, or with a single character. The expressions shown in Table ?? are valid when one of the operands is a typeS, that is a specialization ofsub_match, and the other expression is one of:
(?.?) — a value
xof a typeS, in which caseSTR(x)isx.str();(?.?) — a value
xof typebasic_string<S::value_type, T, A>for any typesTandA, in which caseSTR(x)isbasic_string_view<S::value_type>(x.data(), x.length());(?.?) — a value
xof typebasic_string_view<S::value_type, T>for any typeT, in which caseSTR(x)isbasic_string_view<S::value_type>(x.data(), x.length());(?.?) — a value
xof a type convertible toconst S::value_type*, in which caseSTR(x)isbasic_string_view<S::value_type>(x);(?.?) — a value
xof type convertible toS::value_type, in which caseSTR(x)isbasic_string_view<S::value_type>(&x, 1).
Table ?? — sub_matchcomparisonsExpression Return type Operational
semanticss == tboolSTR(s).compare(STR(t)) == 0s != tboolSTR(s).compare(STR(t)) != 0s < tboolSTR(s).compare(STR(t)) < 0s > tboolSTR(s).compare(STR(t)) > 0s <= tboolSTR(s).compare(STR(t)) <= 0s >= tboolSTR(s).compare(STR(t)) >= 0
[2024-10-03; Jonathan rebases the wording on the latest WP]
The proposed resolution has been implemented and tested in libstdc++.
Proposed resolution:
This wording is relative to N4988.
Change 28.6.3 [re.syn], header <regex> synopsis, as indicated:
using csub_match = sub_match<const char*>; using wcsub_match = sub_match<const wchar_t*>; using ssub_match = sub_match<string::const_iterator>; using wssub_match = sub_match<wstring::const_iterator>; // 28.6.8.3 [re.submatch.op], sub_match non-member operators template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); template<class BiIter, class ST, class SA> bool operator==( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs); template<class BiIter, class ST, class SA> bool operator<=>( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs); template<class BiIter, class ST> bool operator==( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs); template<class BiIter, class ST> bool operator<=>( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs); template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type* rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type* rhs); template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs); template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);
Change 28.6.8 [re.submatch], class template sub_match synopsis, as indicated:
namespace std {
template<class BidirectionalIterator>
class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> {
public:
using value_type =
typename iterator_traits<BidirectionalIterator>::value_type;
[…]
int compare(const sub_match& s) const;
int compare(const string_type& s) const;
int compare(const value_type* s) const;
int compare(basic_string_view<value_type> s) const;
};
}
Change 28.6.8.2 [re.submatch.members] as indicated:
int compare(const value_type* s) const;-7- Returns:
str().compare(s).int compare(basic_string_view<value_type> s) const;-?- Returns:
str().compare(s).
Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:
32.8.3
sub_matchnon-member operators [re.submatch.op]-1- Let
SV(I)bebasic_string_view<typename iterator_traits<I>::value_type>and letSM-CAT(I)becompare_three_way_result_t<basic_string<typename iterator_traits<I>::value_type>SV(I)>template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);-2- Returns:
lhs.compare(rhs) == 0.template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);-3- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(rhs) <=> 0).template<class BiIter, class ST, class SA> bool operator==( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);-4- Returns:
lhs.compare(.typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) == 0template<class BiIter, class ST, class SA> auto operator<=>( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);-5- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) <=> 0 )template<class BiIter, class ST> bool operator==( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);-?- Returns:
lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) == 0.template<class BiIter, class ST> auto operator<=>( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);-?- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) <=> 0).