3126. There's no std::sub_match::compare(string_view) overload

Section: 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.

  1. 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);
      […]
    }
    
  2. 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;
      };
    }
    
  3. 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).

  4. Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:

    31.9.2 sub_match non-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_match provides overloaded relational operators (7.6.9 [expr.rel]) and equality operators (7.6.10 [expr.eq]) for comparisons with another sub_match, with a string, or with a single character. The expressions shown in Table ?? are valid when one of the operands is a type S, that is a specialization of sub_match, and the other expression is one of:

    1. (?.?) — a value x of a type S, in which case STR(x) is x.str();

    2. (?.?) — a value x of type basic_string<S::value_type, T, A> for any types T and A, in which case STR(x) is basic_string_view<S::value_type>(x.data(), x.length());

    3. (?.?) — a value x of type basic_string_view<S::value_type, T> for any type T, in which case STR(x) is basic_string_view<S::value_type>(x.data(), x.length());

    4. (?.?) — a value x of a type convertible to const S::value_type*, in which case STR(x) is basic_string_view<S::value_type>(x);

    5. (?.?) — a value x of type convertible to S::value_type, in which case STR(x) is basic_string_view<S::value_type>(&x, 1).

    Table ?? — sub_match comparisons
    Expression Return type Operational
    semantics
    s == t bool STR(s).compare(STR(t)) == 0
    s != t bool STR(s).compare(STR(t)) != 0
    s < t bool STR(s).compare(STR(t)) < 0
    s > t bool STR(s).compare(STR(t)) > 0
    s <= t bool STR(s).compare(STR(t)) <= 0
    s >= t bool STR(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.

  1. 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);
    
  2. 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;
      };
    }
    
  3. 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).

  4. Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:

    32.8.3 sub_match non-member operators [re.submatch.op]

    -1- Let SV(I) be basic_string_view<typename iterator_traits<I>::value_type> and let SM-CAT(I) be compare_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())) == 0.

    template<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) .