string_viewoperator+ vs. StrCat()absl::Statusstd::bindabsl::optional and std::unique_ptrabsl::StrFormat()make_unique and private Constructors.boolexplicit= delete)switch Statements Responsibly= deleteAbslHashValue and Youcontains()std::optional parametersif and switch statements with initializersinline Variablesstd::unique_ptr Must Be MovedAbslStringify()vector.at()auto for Variable Declarationsstring_viewOriginally 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).