string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable Declarationsstring_view
Originally posted as TotW #1 on April 20, 2012
Updated 2020-08-18
Quicklink: abseil.io/tips/1
string_view
, and Why Should You Care?When creating a function to take a (constant) string as an argument, you have three common alternatives: two that you already know, and one of which you might not be aware:
// C Convention void TakesCharStar(const char* s); // Old Standard C++ convention void TakesString(const std::string& s); // string_view C++ conventions void TakesStringView(absl::string_view s); // Abseil void TakesStringView(std::string_view s); // C++17
The first two cases work best when a caller has the string in the format already
provided, but what happens when a conversion is needed (either from const
char*
to std::string
or std::string
to const char*
)?
Callers needing to convert a std::string
to a const char*
need to use the
(efficient but inconvenient) c_str()
function:
void AlreadyHasString(const std::string& s) { TakesCharStar(s.c_str()); // explicit conversion }
Callers needing to convert a const char*
to a std::string
don’t need to do
anything additional (the good news) but will invoke the creation of a
(convenient but inefficient) temporary string, copying the contents of that
string (the bad news):
void AlreadyHasCharStar(const char* s) { TakesString(s); // compiler will make a copy }
Google’s preferred option for accepting such string parameters is through a
string_view
. This is a “pre-adopted” type from C++17 - for now, use
absl::string_view
even if std::string_view
is available.
An instance of the string_view
class can be thought of as a “view” into an
existing character buffer. Specifically, a string_view
consists of only a
pointer and a length, identifying a section of character data that is not owned
by the string_view
and cannot be modified by the view. Consequently, making a
copy of a string_view
is a shallow operation: no string data is copied.
string_view
has implicit conversion constructors from both const char*
and
const std::string&
, and since string_view
doesn’t copy, there is no O(n)
memory penalty for making a hidden copy. In the case where a const
std::string&
is passed, the constructor runs in O(1) time. In the case where a
const char*
is passed, the constructor invokes a strlen()
automatically (or
you can use the two-parameter string_view
constructor).
void AlreadyHasString(const std::string& s) { TakesStringView(s); // no explicit conversion; convenient! } void AlreadyHasCharStar(const char* s) { TakesStringView(s); // no copy; efficient! }
Because the string_view
does not own its data, any strings pointed to by the
string_view
(just like a const char*
) must have a known lifespan, and must
outlast the string_view
itself.
This means that using string_view
for storage is often questionable: you need
some proof that the underlying data will outlive the string_view
. For example,
the following struct probably doesn’t make sense if it might be stored beyond
the result of a function call that accepts it as a parameter:
struct TestScore { absl::string_view username; // Probably should be a `std::string` double score; };
If your API only needs to reference the string data during a single call, and
doesn’t need to modify the data, accepting a string_view
is sufficient.
If you need to use the data later or modify the data, you can explicitly convert
to a C++ string object using std::string(my_string_view)
. Another option,
discussed in Tip #117, is to pass a std::string
by value and use
std::move
in callers when applicable.
Adding string_view
into an existing codebase is not always the right answer:
changing parameters to pass by string_view
can be inefficient if those are
then passed to a function requiring a std::string
or a NUL-terminated const
char*
. It is best to adopt string_view
starting at the utility code and
working upward, or with complete consistency when starting a new project.
string_view
by value just like
you would pass an int or a double because string_view
is a small value.string_view
as const
only affects whether the string_view
object itself can be modified, and not whether it can be used to modify the
underlying chars – it never can. This is exactly analogous to how a const
char*
can never be used to modify the chars, even if the pointer itself can
be modified.string_view
with const
in
function declarations (see Tip #109). You may use const
to
qualify string_view
in function definitions at your (or your team’s)
discretion, e.g., to be consistent with the surrounding code
(Tip #109). For other local variables, using const
is neither
encouraged nor discouraged
(https://google.github.io/styleguide/cppguide.html#Use_of_const).string_view
is not necessarily NUL-terminated. Thus, it’s not safe to
write:
printf("%s\n", sv.data()); // DON’T DO THIS
Prefer this instead (see Tip #124):
absl::PrintF("%s\n", sv);
You can log a string_view
just like you would a string or a const
char*
:
LOG(INFO) << "Took '" << sv << "'";
You can convert an existing routine that accepts const std::string&
or
NUL-terminated const char*
to string_view
safely in most cases. The only
danger we have encountered in performing this operation is if the address of
the function has been taken, this will result in a build break as the
resulting function-pointer type will be different.
string_view
has a constexpr constructor and a trivial destructor; keep
this in mind when using in static and global variables (see
the style guide)
or constants (see Tip #140).
string_view
is an effective reference and may not be the best choice for
member variables (also see Tip #180).