For fundamental types, a = a + b
and a += b
mean the same thing.
For arbitrary class types, a = a + b
and a += b
are unrelated; they look up different operators, and those operators can do arbitrary things. Them being actually unrelated is code smell, a sign of a design problem.
a = a + b
becomes operator=( a, operator+( a, b ) )
roughly; the actual lookup rules are a bit more complex (involving member operators and non-member operators, and the fact that =
doesn't have a non-member operator, etc), but that is the core of it.
a += b
becomes operator+=( a, b )
in a similar sense.
Now, it is a common pattern to implement +
in terms of +=
; if you do this, you get:
a = a + b
becomes
a = ((auto)(a) += b);
where (auto)
is the new c++20/c++23 "create a temporary copy of the argument" feature.
Fundamentally, a+=b
can reuse the contents of a
directly, while a = a + b
cannot; at the moment a+b
is evaluated, it doesn't know that a
will be soon overwritten.
Some libraries deal with this using a technique known as "expression templates"; a+b
isn't a value, but rather a compile-time description of the expression a+b
, which when assigned to a
is actually used to populate a
with data. With expression templates, the fundamental issue of a+=b
knowing more than a=a+b
is eliminated.
Now, for std::string
specifically, a+b
creates a temporary string object, then a=(a+b)
moves that into a
(it can reuse the buffer of the temporary string object or the buffer of a
, the standard is silent on this matter).
a+=b
must reuse any excess capacity in the a
buffer. So if you a.reserve(1<<30)
(1 billion), a+=b
cannot allocate more.