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.