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 Declarationsoperator+
vs. StrCat()
Originally posted as TotW #3 on May 11, 2012
Updated 2022-11-16
Quicklink: abseil.io/tips/3
Users are often surprised when a reviewer says, “Don’t use the string
concatenation operator, it’s not that efficient.” How can it be that
string::operator+
is inefficient? Isn’t it hard to get that wrong?
It turns out, such inefficiency isn’t clear cut. These two snippets have close to the same execution time, in practice:
std::string foo = LongString1(); std::string bar = LongString2(); std::string foobar = foo + bar; std::string foo = LongString1(); std::string bar = LongString2(); std::string foobar = absl::StrCat(foo, bar);
However, the same is not true for these two snippets:
std::string foo = LongString1(); std::string bar = LongString2(); std::string baz = LongString3(); std::string foobarbaz = foo + bar + baz; std::string foo = LongString1(); std::string bar = LongString2(); std::string baz = LongString3(); std::string foobarbaz = absl::StrCat(foo, bar, baz);
The reason these two cases differ can be understood when we pick apart what is
happening in the expression foo + bar + baz
. Since there are no overloads for
three-argument operators in C++, this operation is necessarily going to make two
calls to string::operator+
. And between those two calls, the operation will
construct (and store) a temporary string. So `std::string foobarbaz = foo + bar
std::string temp = foo + bar; std::string foobarbaz = std::move(temp) + baz;
Specifically, note that the contents of foo
and bar
must be copied to a
temporary location before they are placed within foobarbaz
. (For more on
std::move
, see Tip #77.)
C++11 at least allows the second concatenation to happen without creating a new
string object: std::move(temp) + baz
is equivalent to
std::move(temp.append(baz))
. However, it’s possible that the buffer initially
allocated for the temporary won’t be large enough to hold the final string, in
which case a reallocation (and another copy) will be required. As a result, in
the worst case, chains of n
string concatenations require O(n) reallocations.
It is better instead to use absl::StrCat()
, a nice helper function from
google3/third_party/absl/strings/str_cat.h that calculates the necessary string
length, reserves that size, and concatenates all of the input data into the
output - a well-optimized O(n). Similarly, for cases like:
foobar += foo + bar + baz;
use absl::StrAppend()
, which performs similar optimizations:
absl::StrAppend(&foobar, foo, bar, baz);
In addition, absl::StrCat()
and absl::StrAppend()
operate on types other
than just string types: you can use absl::StrCat
/absl::StrAppend
to convert
int32_t
, uint32_t
, int64_t
, uint64_t
, float
, double
, const char*
,
and string_view
, like this:
std::string foo = absl::StrCat("The year is ", year);
For additional information, see absl::StrCat() and absl::StrAppend() for String Concatenation.