1. Table of Contents
2. Changelog
R1: Removed default parameter fromstd::basic_string::slice(size_t start, size_t end, size_t step)
3. Motivation and Scope
Parsing and string manipulation in C++ used to be very cumbersome, with seemingly basic and trivial methods missing fromstd::basic_string
. The introduction of C++20 and C++23 resolved some of these issues by adding the above listed utility functions.
I believe we can make string manipulation in C++ even better by adding more of these utility functions to std::basic_string
, and one option I always miss, that is present in other programming languages (such as Python), is string-slicing.
Python’s string-slicing is very graceful and easy-to-use, but C++ does not support that syntax.Instead, I propose to add several functions to
std::basic_string
to emulate string-slicing.The functions I propose to add to
std::basic_string
are the following:
namespace std { /* 1. */ basic_string_view basic_string::slice(size_t start, size_t end); /* 2. */ basic_string basic_string::slice(size_t start, size_t end, size_t step); /* 3. */ basic_string_view basic_string::first(size_t count); /* 4. */ basic_string_view basic_string::last(size_t count); }
4. Impact on the Standard
Since these are only trivial functions requiring no major changes to the language or changes to existing API, the impact of this proposal on the standard is minimal.These functions can already be implemented in the current version of C++23 without any extra changes.
Implementation will be left up to the vendor of course, but since these are trivial functions, we can provide a "template" implementation.
5. Design Decisions
There is a choice in whether astd::basic_string
is returned, or a std::basic_string_view
is returned by these new utility functions. It is best for these functions to return
std::basic_string_view
(with the exception of 2 where it is required to return std::basic_string
) since:
-
These functions will most often be used to find something in a string, often not requiring a new dynamic allocation to be made.
-
std::basic_string::contains()
,std::basic_string::starts_with()
andstd::basic_string::ends_with()
all take astd::basic_string_view
as a parameter. Therefore, the return value of the proposed functions matching up with these is a benefit. -
If the user wants a
std::basic_string
instead of astd::basic_string_view
, they can always construct astd::basic_string
.
6. Technical Specifications
-
std::basic_string::slice()
takes 2 parameters:size_t start
andsize_t end
and returns astd::basic_string_view
.-
start
is the starting index (inclusive) of where to start the slice. -
end
is the ending index (exclusive) of where to end the slice.end
is automatically capped t the size of the string.
-
-
std::basic_string::slice()
takes 3 parameters:size_t start
,size_t end
andsize_t step
and returns astd::basic_string
.-
start
is the starting index (inclusive) of where to start the slice. -
end
is the ending index (exclusive) of where to end the slice.end
is automatically capped t the size of the string. -
step
is the increment between each index in the string to be included in the slice.
-
-
std::basic_string::first()
takes 1 parameter:size_t count
and returns astd::basic_string_view
.-
count
is the amount of characters to be included (counting from index 0) in the slice. -
count
is automatically capped at the size of the string and cannot throw an exception or crash.
-
-
std::basic_string::last()
takes 1 parameter:size_t count
and returns astd::basic_string_view
.-
count
is the amount of characters to be included (counting from the last index) in the slice. -
count
is automatically capped at the size of the string and cannot throw an exception or crash.
-
These are easily implemented functions and depend on specific vendor-implementation of std::basic_string
, but here are some sample implementations:
namespace std { // functions should obviously be part of std::basic_string // these are now free functions to show off implementation // 1 std::string_view slice(const std::string& source, size_t start, size_t end) { if (end >= source.size()) { end = source.size(); } return std::string_view(source.data() + start, end - start); } // 2 std::string slice(const std::string& source, size_t start, size_t end, size_t step) { if (end >= source.size()) { end = source.size(); } if (step == 0) { step = 1; } std::string slicedString{}; for (size_t i{ start }; i < end; i += step) { slicedString.push_back(source[i]); } return slicedString; } // 3 std::string_view first(const std::string& source, size_t count) { if (count >= source.size()) { count = source.size(); } return std::string_view(source.data(), count); } // 4 std::string_view last(const std::string& source, size_t count) { if (count >= source.size()) { count = source.size(); } return std::string_view(source.data() + (source.size() - count), count); } }