Document number: | P0457R2 |
Date: | 2017-11-11 |
Project: | LWG |
Reply-to: | Mikhail Maltsev <maltsevm@gmail.com> |
This paper proposes to add member functions starts_with and ends_with to class templates basic_string and basic_string_view. These functions check, whether or not a string starts with a given prefix or ends with a given suffix, respectively.
Checking, whether or not a given string starts with a given prefix (or ends with a given suffix) is a common task. In fact, standard libraries of many other programming languages include routines for performing such checks, for example:
And so on.
Also, some C++ libraries (other than the standard library) that implement a string type include such methods. For example, Qt library has classes QString [3] and QStringRef (analogous to std::string_view) which have startsWith and endsWith member functions.
These functions are widely used. For example, the source code of a recent version of Qt (excluding third-party components) has 1193 occurrences of startsWith and 953 occurrences of endsWith. Other examples include Webkit (304 occurrences of startsWith and 142 occurrences of endsWith) and LLVM (class StringRef, 113 matches for StartsWith and 38 matches for EndsWith).
The basic_string_view class template included starts_with and ends_with up to revision 3 of the proposal (N3609 [4]). These two member functions were removed after LEWG discussion in Bristol. The main concerns were:
This proposal adds member functions starts_with and ends_with to class templates basic_string and basic_string_view. Another considered option was to add free functions to namespace std, but adding member functions is consistent with the existing API for compare. Besides, as the original proposal [5] mentioned, the order of parameters of a free function is ambiguous (starts_with(string, prefix) vs starts_with(prefix, string)).
The original string_view proposal included the following overloads of starts_with:
// basic_string: bool starts_with(basic_string_view<charT, traits> s) const noexcept; bool starts_with(charT c) const noexcept; bool starts_with(const charT* s) const noexcept; // basic_string_view: bool starts_with(const basic_string_view& prefix) const noexcept;
Qt offers the following overload set (for QString and QStringRef):
bool startsWith(const QString & s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const bool startsWith(const QLatin1String & s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const bool startsWith(const QChar & c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const bool startsWith(const QStringRef & s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
This proposal includes the following overloads:
// basic_string: bool starts_with(charT x) const noexcept; bool starts_with(basic_string_view<charT, traits> x) const noexcept; bool starts_with(const charT* x) const; // basic_string_view: constexpr bool starts_with(charT x) const noexcept; constexpr bool starts_with(basic_string_view<charT, traits> x) const noexcept; constexpr bool starts_with(const charT* x) const;
The overload overload for basic_string is not included, because basic_string has a non-explicit conversion operator to basic_string_view.
In accordance with the guidance from P0254R1 [5], objects of type basic_string_view are passed by value (not by reference).
In [basic.string], add:
bool starts_with(basic_string_view<charT, traits> x) const noexcept; bool starts_with(charT x) const noexcept; bool starts_with(const charT* x) const; bool ends_with(basic_string_view<charT, traits> x) const noexcept; bool ends_with(charT x) const noexcept; bool ends_with(const charT* x) const;
After [string::compare] add:
basic_string::starts_with [string.starts_with]
bool starts_with(basic_string_view<charT, traits> x) const noexcept; bool starts_with(charT x) const noexcept; bool starts_with(const charT* x) const;
Effects: Equivalent to:
return basic_string_view<charT, traits>(data(), size()).starts_with(x);
basic_string::ends_with [string.ends_with]
bool ends_with(basic_string_view<charT, traits> x) const noexcept; bool ends_with(charT x) const noexcept; bool ends_with(const charT* x) const;
Effects: Equivalent to:
return basic_string_view<charT, traits>(data(), size()).ends_with(x);
In [string.view.template], add:
constexpr bool starts_with(basic_string_view x) const noexcept; constexpr bool starts_with(charT x) const noexcept; constexpr bool starts_with(const charT* x) const; constexpr bool ends_with(basic_string_view x) const noexcept; constexpr bool ends_with(charT x) const noexcept; constexpr bool ends_with(const charT* x) const;
In [string.view.ops], add:
constexpr bool starts_with(basic_string_view x) const noexcept;
Effects: Equivalent to: return compare(0, npos, x) == 0;
constexpr bool starts_with(charT x) const noexcept;
Effects: Equivalent to: return starts_with(basic_string_view(&x, 1));
constexpr bool starts_with(const charT* x) const;
Effects: Equivalent to: return starts_with(basic_string_view(x));
constexpr bool ends_with(basic_string_view x) const noexcept;
Effects: Equivalent to: return size() >= x.size() && compare(size() - x.size(), npos, x) == 0;
constexpr bool ends_with(charT x) const noexcept;
Effects: Equivalent to: return ends_with(basic_string_view(&x, 1));
constexpr bool ends_with(const charT* x) const;
Effects: Equivalent to: return ends_with(basic_string_view(x));