string_view
: a non-owning reference to a string, revision 7References to strings are very common in C++ programs, but often the callee doesn't care about the exact type of the object that owns the data. 3 things generally happen in this case:
const std::string&
and insists
that callers copy the data if it was originally owned by another
type.char*
and
a length (or just char*
and assumes
0-termination)—and reduces the readability and safety of
calls and loses any helper functions the original type
provided.Google, LLVM, and Bloomberg have independently implemented a
string-reference type to encapsulate this kind of argument. string_view
is implicitly
constructible from const char*
and std::string
. It
provides the const
member operations from
std::string
to ease conversion. This paper follows Chromium
and Bloomberg
in extending string_view
to basic_string_view<charT>
, and
further extends it to include a traits
parameter to match
basic_string
. We provide typedefs to parallel the 4
basic_string
typedefs.
Both Google's and LLVM's string_view
types (but not
Bloomberg's) extend the interface from std::string
to provide
some helpful utility functions:
Versions of std::string
operations that take
string_view
instead also give the standard a way to provide
in-place operations on non-null-terminated byte/character sequences:
Google's StringPiece
provides as_string
and
ToString
methods to convert to std::string
. LLVM's
StringRef
provides both a str()
explicit
conversion and an implicit operator std::string()
. Since this
paper builds on top of C++11, we provide an explicit
conversion
constructor as well as a less verbose to_string
function.
None of the existing classes have constexpr
methods.
We found rough consensus
around renaming this class to string_view
. Other options
included:
The interface of string_view
is similar to, but not exactly
the same as the interface of std::string
. In general, we want
to minimize differences between std::string
and
string_view
so that users can go back and forth between the two
often. This section justifies the differences whose utility we think
overcomes that general rule.
remove_prefix()
and
remove_suffix()
make
it easy to parse strings using string_view
. They could both
be implemented as non-member functions (e.g. str.remove_prefix(n)
=== str = str.substr(n)
), but it seems useful to
provide the simplest mutators as member functions. Note that other
traversal primitives need to be non-members so that they're
extensible, which may argue for pulling these out too.I haven't taken every suggestion to change string_view
. This
section explains the rationales.
Many people have asked why we aren't removing all of the
find*
methods, since they're widely considered a
wart on std::string
. First, we'd like to make it as easy as
possible to convert code to use string_view
, so it's useful to
keep the interface as similar as reasonable to std::string
.
Second, replacing these these methods with uses of the standard algorithms
library requires switching from indices to iterators, writing
somewhat-complicated conversion code, and/or passing custom lambdas to
find_if
. Let's look at the replacement code for each of the
remaining methods:
haystack.find(needle)
auto iter = std::search(haystack.begin(), haystack.end(),
needle.begin(), needle.end());
return iter == haystack.end() ? std::string::npos : iter - haystack.begin();
haystack.rfind(needle)
auto iter = std::find_end(haystack.begin(), haystack.end(),
needle.begin(), needle.end());
return iter == haystack.end() ? std::string::npos : iter - haystack.begin();
haystack.find_first_of(needles)
auto iter = std::find_first_of(haystack.begin(), haystack.end(),
needles.begin(), needles.end());
return iter == haystack.end() ? std::string::npos : iter - haystack.begin();
haystack.find_last_of(needles)
auto iter = std::find_first_of(haystack.rbegin(), haystack.rend(),
needles.begin(), needles.end());
return iter == haystack.rend() ? std::string::npos : iter.base() - 1 - haystack.begin();
haystack.find_first_not_of(straw)
auto iter = std::find_if(haystack.begin(), haystack.end(), [&](char c) {
return std::find(straw.begin(), straw.end(), c) == straw.end();
});
return iter == haystack.end() ? std::string::npos : iter - haystack.begin();
haystack.find_last_not_of(straw)
auto iter = std::find_if(haystack.rbegin(), haystack.rend(), [&](char c) {
return std::find(straw.begin(), straw.end(), c) == straw.end();
});
return iter == haystack.rend() ? std::string::npos : iter.base() - 1 - haystack.begin();
find
, rfind
, and find_first_of
are straightforward, although the conversion from indices to iterators
would prevent many users from switching even to them.
find_last_of
, find_first_not_of
, and
find_last_not_of
get progressively worse to handle even in an
iterator-based function.
Discussion in Bristol concluded that string_view
should
include all of the const signatures from string
.
SF | WF | N | WA | SA |
5 | 4 | 1 | 2 | 0 |
SF | WF | N | WA | SA |
4 | 2 | 2 | 3 | 2 |
basic_string_view<char>
mutable… and use basic_string_view<const char>
for the constant
case. The constant case is enough more common than the mutable case that
it needs to be the default. Making the mutable case the default would
prevent passing string literals into string_view
parameters,
which would defeat a significant use case for string_view
. In
a somewhat analogous sitation, LLVM defined an ArrayRef
class in Feb 2011, and didn't find a need for the matching
MutableArrayRef
until Jan 2012. They still haven't needed a
mutable version of StringRef
.
One possible reason for this is that most uses that need to modify a
string also need to be able to change its length, and that's impossible
through even a mutable version of string_view
.
We could use typedef basic_string_view<const char>
string_view
to make the immutable case the default while still
supporting the mutable case using the same template. I haven't gone this
way because it would complicate the template's definition without
significantly helping users.
explicit operator bool
This would be an abbreviation for !empty()
, usable for
initialization in if
statements. N3509
came to SG9 in Bristol and was not accepted.
SF | WF | WA | SA |
0 | 1 | 3 | 5 |
strlen("string literal")
With a constructor of the form:
template<size_t N>
basic_string_view(const charT (&str)[N]);
we could avoid a strlen()
call when a
basic_string_view
is constructed from a string literal.
Unfortunately, this constructor does completely the wrong thing when
called like:
char space[PATH_MAX];
snprintf(space, sizeof(space), "some string");
string_view str(space);
It would be possible to avoid that problem by defining a
basic_string_view(char* str)
that uses strlen()
again, but this adds complexity. Some people have suggested a
string_view::from_literal
method, but I consider that too
verbose.
Even the original worry is obsolete given modern optimizers: both gcc
and clang optimize strlen("Literal")
into a constant, making
the simple, safe code as efficient as the template. Other implementations
should provide the same optimization as a QoI issue.
begin
/end
instead of the elementsOperations on string_view
apply to the characters in the
string, and not the pointers that refer to the characters. This
introduces the possibility that the underlying characters might change
while a string_view
referring to them is in an associative
container, which would break the container, but we believe this risk is
worthwhile because it matches existing practice and matches user
intentions more often.
contiguous_range<charT>
contiguous_range<T>
along with an
is_contiguous<IteratorOrRange>
trait would be useful for
many purposes. However, a reference class that's specifically for strings
provides a couple extra benefits:
string_view
can have an implicit conversion from
const char*
, while it would be a surprising special case to
provide that on contiguous_range<const char*>
.basic_string
's interface to
ease transitions to and from ownership, while such methods would be very
strange on contiguous_range
.basic_string_view
takes a char_traits
argument allowing customization of comparison.
contiguous_range
likely wouldn't.string_view
s using the elements they
refer to. There's a stronger argument to compare a
contiguous_range
using the pointers inside it, meaning two
contiguous_range<char>
s of the same characters might
compare unequal.std::string
in
addition to std::vector<char>
. Users benefit from
saying which they mean in interfaces.string_view
null-terminatedDoing this naively makes substr
impossible to implement
without a copy. We could imagine inventing a more complex interface that
records whether the input string was null-terminated, giving the user the
option to use that string when trying to pass a string_view
to
a legacy or C function expecting a null-terminated const
char*
. This proposal doesn't include such an interface because it
would make string_view
bigger or more expensive, and because
there's no existing practice to guide us toward the right interface.
Another option would be to define a separate zstring_view
class to represent null-terminated strings and let it decay to
string_view
when necessary. That's plausible but not part of
this proposal.
In Kona
2012, I proposed a range<>
class with
pop_front
, etc. members that adjusted the bounds of the
range. Discussion there indicated that committee members were
uncomfortable using the same names for lightweight range operations as
container operations. Existing practice doesn't agree on a name for this
operation, so I've kept the name used by Google's
StringPiece
.
Beman
Dawes suggested defining std::string_view_{begin,end}
and
allowing users to add overloads within std
. Using ADL is a
slight variant. We could also allow conversion from any type with
.data()
and .size()
members returning the right
types.
Ultimately, I think we want to allow this conversion based on detecting contiguous ranges. Any constructor we add to work around that is going to look like a wart in a couple years. I think we'll be better off making users explicitly convert when they can't add an appropriate conversion operator, and then we can add the optimal constructor when contiguous iterators make it into the library.
SF | WF | N | WA | SA |
0 | 0 | 1 | 5 | 6 |
string_view
. I'll propose those changes in a
subsequent paper based on the part
of N3685 that isn't in this paper.string_view
, maybe ""sv
.This paper updates N3849 by:
to_string
to be a member function, similar to
bitset
.pointer
and reference
to be
non-const
types, for consistency with the containers,
including set
.constexpr
in a few places I missed, but the LWG
pointed out.string_view(nullptr, 0)
, as voted by a mixed
LEWG/LWG group.return ...
" to
"Effects: Equivalent to ...
" on the theory that "if F
has no Returns: element, a non-void return from F is specified by
the Returns: elements in the code sequence." means this is ok, and
if not, that wording should be improved.copy()
.N3849 updated N3762 by:
string_view().data() != nullptr
and adding
an alternate wording section in case we decide that should be
possible,constexpr
that
could be a constant expression if the traits
class
is suitable, anddata_
and size_
members, and re-explaining several members in terms of them.N3762 updated N3685 by removing other standard library updates so
that the core string_view
class can be accepted independently.
I've also:
pos
and n
parameters,copy()
back,swap()
since it's constant time,std::swap
and the range access functions are
available when <string_view>
is included, andnoexcept
s.N3685 updated N3609 with the results of the LEWG discussion in Bristol. Significant changes include:
<string_view>
header.pos
and n
parameters back to string_view
methods.starts_with
and ends_with
.N3609 was a minor update to N3512 that renamed the proposed class to
basic_string_view
and fixed some wording mistakes.
N3512 updated N3442 with wording for the draft C++14 standard. Note that we still aren't sure whether we're aiming for a TS or C++14.
N3442
was aimed at a TS and updated
N3334
by removing array_ref
.
The most recent version of this paper is maintained on GitHub.
The class template basic_string_view
describes an object
that can refer to a constant contiguous sequence of char-like
(C++11[strings.general]) objects with the first element of the sequence at
position zero. In the rest of this section, the type of the char-like
objects held in a basic_string_view
object is designated by
charT
.
[Note: The library provides implicit conversions from const
charT*
and std::basic_string<charT, ...>
to
std::basic_string_view<charT, ...>
so that user code can
accept just std::basic_string_view<charT>
as a non-templated
parameter wherever a sequence of characters is expected. User-defined types
should define their own implicit conversions to
std::basic_string_view
in
order to interoperate with these functions. — end note ]
The complexity of basic_string_view
member functions is O(1)
unless otherwise specified.
namespace std {
namespace experimental {
inline namespace fundamentals_v1 {
// [basic.string.view], basic_string_view:
template<class charT, class traits = char_traits<charT>>
class basic_string_view;
// [string.view.comparison], non-member basic_string_view 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 bool operator!=(basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) noexcept;
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 bool operator> (basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) noexcept;
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 bool operator>=(basic_string_view<charT, traits> x, basic_string_view<charT, traits> y) noexcept;
// see below, sufficient additional overloads of comparison functions
// [string.view.io], Inserters and extractors
template<class charT, class traits>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
basic_string_view<charT, traits> str);
// basic_string_view typedef names
typedef basic_string_view<char> string_view;
typedef basic_string_view<char16_t> u16string_view;
typedef basic_string_view<char32_t> u32string_view;
typedef basic_string_view<wchar_t> wstring_view;
} // namespace fundamentals_v1
} // namespace experimental
// [string.view.hash], hash support:
template <class T> struct hash;
template <> struct hash<experimental::string_view>;
template <> struct hash<experimental::u16string_view>;
template <> struct hash<experimental::u32string_view>;
template <> struct hash<experimental::wstring_view>;
} // namespace std
The function templates defined in C++11[utility.swap] and C++11[iterator.range] are
available when <string_view>
is included.
Normally I would update the list in C++11[iterator.range], but we're not yet sure how to do that in a TS, so I picked the more self-contained option.
namespace std {
namespace experimental {
namespace fundamentals_v1 {
template<class charT, class traits = char_traits<charT>>
class basic_string_view {
public:
// types
typedef traits traits_type;
typedef charT value_type;
typedef charT* pointer;
typedef const charT* const_pointer;
typedef charT& reference;
typedef const charT& const_reference;
typedef implementation-defined const_iterator; // See [string.view.iterators]
typedef const_iterator iterator; // [Footnote: Because basic_string_view refers to a constant sequence, iterator and const_iterator are the same type. --end footnote]
typedef reverse_iterator<const_iterator> const_reverse_iterator;
typedef const_reverse_iterator reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
static constexpr size_type npos = size_type(-1);
// [string.view.cons], construct/copy
constexpr basic_string_view() noexcept;
constexpr basic_string_view(const basic_string_view&) noexcept = default;
basic_string_view& operator=(const basic_string_view&) noexcept = default;
template<class Allocator>
basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;
constexpr basic_string_view(const charT* str);
The above constructor is constexpr to take advantage of user-written traits classes that might provide a constexpr length()
function. It can't be part of a constant expression with std::char_traits<char>
without changes to that class.
constexpr basic_string_view(const charT* str, size_type len);
No initializer_list constructor because C++11[dcl.init.list]p6 says it would likely store a dangling reference into the basic_string_view
.
// [string.view.iterators], iterators
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr const_iterator cbegin() const noexcept;
constexpr const_iterator cend() const noexcept;
reverse_iterator methods aren’t constexpr because reverse_iterator isn’t a literal type. See LWG Issue 2208.
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
// [string.view.capacity], capacity
constexpr size_type size() const noexcept;
constexpr size_type length() const noexcept;
constexpr size_type max_size() const noexcept;
constexpr bool empty() const noexcept;
// [string.view.access], element access
constexpr const_reference operator[](size_type pos) const;
constexpr const_reference at(size_type pos) const;
constexpr const_reference front() const;
constexpr const_reference back() const;
constexpr const_pointer data() const noexcept;
// [string.view.modifiers], modifiers:
constexpr void clear() noexcept;
constexpr void remove_prefix(size_type n);
constexpr void remove_suffix(size_type n);
constexpr void swap(basic_string_view& s) noexcept;
// [string.view.ops], string operations:
template<class Allocator>
explicit operator basic_string<charT, traits, Allocator>() const;
template<class Allocator = allocator<charT> >
basic_string<charT, traits, Allocator> to_string(
const Allocator& a = Allocator()) const;
size_type copy(charT* s, size_type n, size_type pos = 0) const;
constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
constexpr int compare(basic_string_view s) const noexcept;
constexpr int compare(size_type pos1, size_type n1, basic_string_view s) const;
constexpr int compare(size_type pos1, size_type n1,
basic_string_view s, size_type pos2, size_type n2) const;
constexpr int compare(const charT* s) const;
constexpr int compare(size_type pos1, size_type n1, const charT* s) const;
constexpr int compare(size_type pos1, size_type n1,
const charT* s, size_type n2) const;
constexpr size_type find(basic_string_view s, size_type pos = 0) const noexcept;
constexpr size_type find(charT c, size_type pos = 0) const noexcept;
constexpr size_type find(const charT* s, size_type pos, size_type n) const;
constexpr size_type find(const charT* s, size_type pos = 0) const;
constexpr size_type rfind(basic_string_view s, size_type pos = npos) const noexcept;
constexpr size_type rfind(charT c, size_type pos = npos) const noexcept;
constexpr size_type rfind(const charT* s, size_type pos, size_type n) const;
constexpr size_type rfind(const charT* s, size_type pos = npos) const;
constexpr size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept;
constexpr size_type find_first_of(charT c, size_type pos = 0) const noexcept;
constexpr size_type find_first_of(const charT* s, size_type pos, size_type n) const;
constexpr size_type find_first_of(const charT* s, size_type pos = 0) const;
constexpr size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept;
constexpr size_type find_last_of(charT c, size_type pos = npos) const noexcept;
constexpr size_type find_last_of(const charT* s, size_type pos, size_type n) const;
constexpr size_type find_last_of(const charT* s, size_type pos = npos) const;
constexpr size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept;
constexpr size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;
constexpr size_type find_first_not_of(const charT* s, size_type pos, size_type n) const;
constexpr size_type find_first_not_of(const charT* s, size_type pos = 0) const;
constexpr size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept;
constexpr size_type find_last_not_of(charT c, size_type pos = npos) const noexcept;
constexpr size_type find_last_not_of(const charT* s, size_type pos, size_type n) const;
constexpr size_type find_last_not_of(const charT* s, size_type pos = npos) const;
private:
const_pointer data_; // exposition only
size_type size_; // exposition only
};
} // namespace fundamentals_v1
} // namespace experimental
} // namespace std
In every specialization basic_string_view<charT, traits>
,
the type traits
shall satisfy the character traits
requirements (C++11[char.traits]), and the type traits::char_type
shall name the same type as charT
.
constexpr basic_string_view() noexcept;
Effects: Constructs an empty basic_string_view
.
Postcondition: size_ == 0
, and data_ == nullptr
.
template<class Allocator>
basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;
Effects: Constructs a basic_string_view
, with the postconditions in Table [tab:string.view.ctr.1]
Element | Value |
---|---|
data_ | str.data() |
size_ | str.size() |
constexpr basic_string_view(const charT* str);
Requires: [str
,str + traits::length(str)
) is a valid range.
Effects: Constructs a basic_string_view
referring to the same string as str
, with the postconditions
in Table [tab:string.view.ctr.2]
Element | Value |
---|---|
data_ | str |
size_ | traits::length(str) |
Complexity: O(traits::length(str)
)
constexpr basic_string_view(const charT* str, size_type len);
Requires: [str
,str + len
) is a valid range.
Effects: Constructs a basic_string_view
, with the postconditions in Table [tab:string.view.ctr.3]
Element | Value |
---|---|
data_ | str |
size_ | len |
typedef implementation-defined const_iterator;
A constant random-access iterator type such that, for a const_iterator it
,
if &*(it+N)
is valid, then it is equal to (&*it)+N
.
For a basic_string_view str
, any operation that invalidates a pointer in
the range [str.data()
, str.data()+str.size()
) invalidates pointers,
iterators, and references returned from str
's methods.
All requirements on container iterators (C++11[container.requirements])
apply to basic_string_view::const_iterator
as well.
constexpr const_iterator begin() const noexcept;
constexpr const_iterator cbegin() const noexcept;
Returns: An iterator such that &*begin() == data_
if !empty()
, or else an unspecified value such that [begin()
,end()
) is a valid range.
constexpr const_iterator end() const noexcept;
constexpr const_iterator cend() const noexcept;
Returns: begin() + size()
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator crbegin() const noexcept;
Returns: const_reverse_iterator(end())
.
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crend() const noexcept;
Returns: const_reverse_iterator(begin())
.
constexpr size_type size() const noexcept;
Returns: size_
constexpr size_type length() const noexcept;
Returns: size_
.
constexpr size_type max_size() const noexcept;
Returns: The largest possible number of char-like objects that can be referred to by a basic_string_view
.
constexpr bool empty() const noexcept;
Returns: size_ == 0
.
constexpr const_reference operator[](size_type pos) const;
Requires: pos < size()
.
Returns: data_[pos]
Throws: Nothing.
[ Note: Unlike basic_string::operator[]
,
basic_string_view::operator[](size())
has undefined behavior instead of
returning charT()
. — end note ]
constexpr const_reference at(size_type pos) const;
Throws: out_of_range
if pos >= size()
.
Returns: data_[pos]
.
constexpr const_reference front() const;
Requires: !empty()
Returns: data_[0]
.
Throws: Nothing.
constexpr const_reference back() const;
Requires: !empty()
Returns: data_[size() - 1]
.
Throws: Nothing.
constexpr const_pointer data() const noexcept;
Returns: data_
[ Note: Unlike basic_string::data()
and string literals, data()
may
return a pointer to a buffer that is not null-terminated. Therefore it is
typically a mistake to pass data()
to a routine that takes just a
const charT*
and expects a null-terminated string. — end note ]
constexpr void clear() noexcept;
Effects: Equivalent to *this = basic_string_view()
constexpr void remove_prefix(size_type n);
Requires: n <= size()
Effects: Equivalent to data_ += n; size_ -= n;
constexpr void remove_suffix(size_type n);
Requires: n <= size()
Effects: Equivalent to size_ -= n;
constexpr void swap(basic_string_view& s) noexcept;
Effects: Exchanges the values of *this
and s
.
template<class Allocator>
explicit // Footnote: This conversion is explicit to avoid accidental O(N) operations on type mismatches. --end footnote
operator basic_string<charT, traits, Allocator>() const;
Effects: Equivalent to basic_string<charT, traits, Allocator>(begin(), end()).
Complexity: O(size()
)
[ Note: Users who want to control the allocator instance should call to_string(allocator)
. -- end note ]
template<class Allocator = allocator<charT> >
basic_string<charT, traits, Allocator> to_string(
const Allocator& a = Allocator()) const;
Returns: basic_string<charT, traits, Allocator>(begin(), end(), a)
.
Complexity: O(size()
)
size_type copy(charT* s, size_type n, size_type pos = 0) const;
Let rlen
be the smaller of n
and size() - pos
.
Throws: out_of_range
if pos > size()
.
Requires: [s
, s+rlen
) is a valid range.
Effects: Equivalent to std::copy_n(begin() + pos, rlen, s).
Returns: rlen
.
Complexity: O(rlen
)
constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
Throws: out_of_range
if pos > size()
.
Effects: Determines the effective length rlen
of the string to reference as the smaller of n
and size() - pos
.
Returns: basic_string_view(data()+pos, rlen)
.
constexpr int compare(basic_string_view str) const noexcept;
Effects: Determines the effective length
rlen
of the strings to compare as the
smaller of size()
and str.size()
. The
function then compares the two strings by calling
traits::compare(data(), str.data(), rlen)
.
Complexity: O(rlen
)
Returns: The nonzero result if the result of the comparison is nonzero. Otherwise, returns a value as indicated in Table [tab:string.view.compare].
Condition | Return Value |
---|---|
size() < str.size() | < 0 |
size() == str.size() | 0 |
size() > str.size() | > 0 |
constexpr int compare(size_type pos1, size_type n1, basic_string_view str) const;
Effects: Equivalent to substr(pos1, n1).compare(str)
.
constexpr int compare(size_type pos1, size_type n1, basic_string_view str,
size_type pos2, size_type n2) const;
Effects: Equivalent to substr(pos1, n1).compare(str.substr(pos2, n2))
.
constexpr int compare(const charT* s) const;
Effects: Equivalent to compare(basic_string_view(s))
.
constexpr int compare(size_type pos1, size_type n1, const charT* s) const;
Effects: Equivalent to substr(pos1, n1).compare(basic_string_view(s))
.
constexpr int compare(size_type pos1, size_type n1,
const charT* s, size_type n2) const;
Effects: Equivalent to substr(pos1, n1).compare(basic_string_view(s, n2))
.
This section specifies the basic_string_view
member
functions named find
, rfind
,
find_first_of
, find_last_of
,
find_first_not_of
, and find_last_not_of
.
Member functions in this section have complexity O(size() * str.size()
)
at worst, although implementations are encouraged to do better.
Each member function of the form
constexpr return-type fx1(const charT* s, size_type pos);
is equivalent to fx1(basic_string_view(s), pos)
.
Each member function of the form
constexpr return-type fx1(const charT* s, size_type pos, size_type n);
is equivalent to fx1(basic_string_view(s, n), pos)
.
Each member function of the form
constexpr return-type fx2(charT c, size_type pos);
is equivalent to fx2(basic_string_view(&c, 1), pos)
.
constexpr size_type find(basic_string_view str, size_type pos = 0) const noexcept;
Effects: Determines the lowest position xpos
, if possible, such that the following conditions obtain:
pos <= xpos
xpos + str.size() <= size()
traits::eq(at(xpos+I), str.at(I))
for all elements I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
constexpr size_type rfind(basic_string_view str, size_type pos = npos) const noexcept;
Effects: Determines the highest position xpos
, if possible, such that the following conditions obtain:
xpos <= pos
xpos + str.size() <= size()
traits::eq(at(xpos+I), str.at(I))
for all elements I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept;
Effects: Determines the lowest position xpos
, if possible, such that the following conditions obtain:
pos <= xpos
xpos < size()
traits::eq(at(xpos), str.at(I))
for some element I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
constexpr size_type find_last_of(basic_string_view str, size_type pos = npos) const noexcept;
Effects: Determines the highest position xpos
, if possible, such that the following conditions obtain:
xpos <= pos
xpos < size()
traits::eq(at(xpos), str.at(I))
for some element I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept;
Effects: Determines the lowest position xpos
, if possible, such that the following conditions obtain:
pos <= xpos
xpos < size()
traits::eq(at(xpos), str.at(I))
for no element I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = npos) const noexcept;
Effects: Determines the highest position xpos
, if possible, such that the following conditions obtain:
xpos <= pos
xpos < size()
traits::eq(at(xpos), str.at(I))
for no element I
of the string referenced by str
.Returns: xpos
if the function can determine such a value for xpos
. Otherwise, returns npos
.
Remarks: Uses traits::eq()
.
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
[tab:string.view.comparison.overloads].
Expression | Equivalent to |
---|---|
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: A sample conforming implementation for operator== would be:
template<class T> using __identity = typename std::decay<T>::type;
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,
__identity<basic_string_view<charT, traits>> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template<class charT, class traits>
constexpr bool operator==(
__identity<basic_string_view<charT, traits>> lhs,
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,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) == 0
.
template<class charT, class traits>
constexpr bool operator!=(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) != 0
.
template<class charT, class traits>
constexpr bool operator< (basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) < 0
.
template<class charT, class traits>
constexpr bool operator> (basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) > 0
.
template<class charT, class traits>
constexpr bool operator<=(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) <= 0
.
template<class charT, class traits>
constexpr bool operator>=(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept;
Returns: lhs.compare(rhs) >= 0
.
template<class charT, class traits>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
basic_string_view<charT, traits> str);
Effects: Equivalent to os << str.to_string()
.
template <> struct hash<experimental::string_view>;
template <> struct hash<experimental::u16string_view>;
template <> struct hash<experimental::u32string_view>;
template <> struct hash<experimental::wstring_view>;
The template specializations shall meet the requirements of class template hash (C++11[unord.hash]).
I'd like to thank Marshall Clow, Olaf van der Spek, the Boost and std-proposals mailing lists, Chandler Carruth, Beman Dawes, Alisdair Meredith, and especially Daniel Krügler for help, advice, and wording in this paper.