std::operat...asic_string)(3)
π std::operator+(std::basic_string) β the C++ Standard Library one
okay bestie, sit down, because this one is long and it's giving "the standard library said yes to everything." This is std::operator+(std::basic_string), live from <string>, and it's literally just the + operator for strings. Glue two strings together. That's the whole vibe. But because it's C++, there are sixteen overloads and a whole essay about allocators. We love that for us. π
β¨ Synopsis β¨
Defined in header <string>
Every overload is a template< class CharT, class Traits, class Alloc >. Here they are, all 16 of them, no skips, no cuts. π
(1) β constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const std::basic_string<CharT,Traits,Alloc>& lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
(2) β constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const std::basic_string<CharT,Traits,Alloc>& lhs,
const CharT* rhs );
(3) β constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const std::basic_string<CharT,Traits,Alloc>& lhs,
CharT rhs );
(4) β since C++26
constexpr std::basic_string<CharT,Traits,Alloc>
operator+( const std::basic_string<CharT,Traits,Alloc>& lhs,
std::type_identity_t<std::basic_string_view<CharT,Traits>> rhs );
(5) β constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const CharT* lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
(6) β constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( CharT lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
(7) β since C++26
constexpr std::basic_string<CharT,Traits,Alloc>
operator+(
std::type_identity_t<std::basic_string_view<CharT,Traits>> lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
(8) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( std::basic_string<CharT,Traits,Alloc>&& lhs,
std::basic_string<CharT,Traits,Alloc>&& rhs );
(9) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( std::basic_string<CharT,Traits,Alloc>&& lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
(10) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( std::basic_string<CharT,Traits,Alloc>&& lhs,
const CharT* rhs );
(11) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( std::basic_string<CharT,Traits,Alloc>&& lhs,
CharT rhs );
(12) β since C++26
constexpr std::basic_string<CharT,Traits,Alloc>
operator+( std::basic_string<CharT,Traits,Alloc>&& lhs,
std::type_identity_t<std::basic_string_view<CharT,Traits>> rhs );
(13) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const std::basic_string<CharT,Traits,Alloc>& lhs,
std::basic_string<CharT,Traits,Alloc>&& rhs );
(14) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( const CharT* lhs,
std::basic_string<CharT,Traits,Alloc>&& rhs );
(15) β since C++11, constexpr since C++20
std::basic_string<CharT,Traits,Alloc>
operator+( CharT lhs,
std::basic_string<CharT,Traits,Alloc>&& rhs );
(16) β since C++26
constexpr std::basic_string<CharT,Traits,Alloc>
operator+(
std::type_identity_t<std::basic_string_view<CharT,Traits>> lhs,
std::basic_string<CharT,Traits,Alloc>&& rhs );
πΈ What it even does πΈ
It returns a string containing the characters from lhs followed by the characters from rhs. That's it. That's the function. Concatenation. Iconic, honestly. β¨
Equivalent to: (this is the receipts, bestie β the standard literally tells you what it's doing under the hood π)
- and 2. β
std::basic_string<CharT, Traits, Allocator> r = lhs; r.append(rhs); return r; - β
std::basic_string<CharT, Traits, Allocator> r = lhs; r.push_back(rhs); return r; - β
std::basic_string<CharT, Traits, Allocator> r = lhs; r.append(rhs); return r; - β
std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(0, lhs); return r; - β
std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(r.begin(), lhs); return r; - β
std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(0, lhs); return r; - β
lhs.append(rhs); return std::move(lhs);β except that both lhs and rhs are left in valid but unspecified states. And if lhs and rhs have equal allocators, the implementation can move from either. π (unspecified state is giving "she's fine, don't worry about it") - and 10. β
lhs.append(rhs); return std::move(lhs); - β
lhs.push_back(rhs); return std::move(lhs); - β
lhs.append(rhs); return std::move(lhs); - and 14. β
rhs.insert(0, lhs); return std::move(rhs); - β
rhs.insert(rhs.begin(), lhs); return std::move(rhs); - β
rhs.insert(0, lhs); return std::move(rhs);
π§ The allocator used for the result is:
- 1β4)
std::allocator_traits<Alloc>::select_on_container_copy_construction(lhs.get_allocator()) - 5β7)
std::allocator_traits<Alloc>::select_on_container_copy_construction(rhs.get_allocator()) - 8β12)
lhs.get_allocator() - 13β16)
rhs.get_allocator()
π¦ In other words (since C++11):
- If one operand is a
basic_stringrvalue, its allocator is used. π - Otherwise,
select_on_container_copy_constructionis used on the allocator of the lvaluebasic_stringoperand.
In each case, the left operand is preferred when both are basic_strings of the same value category.
For (8β16), all rvalue basic_string operands are left in valid but unspecified states.
π Parameters π
- lhs β
string,string view(since C++26), character, or pointer to the first character in a null-terminated array - rhs β
string,string view(since C++26), character, or pointer to the first character in a null-terminated array
π Return value π
A string containing characters from lhs followed by the characters from rhs, using the allocator determined as above (since C++11).
π Notes π
operator+ should be used with great caution when stateful allocators are involved (such as when std::pmr::string is used) (since C++17). Prior to P1165R1, the allocator used for the result was determined by historical accident and can vary from overload to overload for no apparent reason. π (not "no apparent reason" being an actual thing in the standard, I can't)
Because the allocator used by the result of operator+ is sensitive to value category, operator+ is not associative with respect to allocator propagation:
using my_string = std::basic_string<char, std::char_traits<char>, my_allocator<char>>;
my_string cat();
const my_string& dog();
my_string meow = /* ... */, woof = /* ... */;
meow + cat() + /* ... */; // uses select_on_container_copy_construction on meow's allocator
woof + dog() + /* ... */; // uses allocator of dog()'s return value instead
(since C++11)
meow + woof + meow; // uses select_on_container_copy_construction on meow's allocator
meow + (woof + meow); // uses SOCCC on woof's allocator instead
For a chain of operator+ invocations, the allocator used for the ultimate result may be controlled by prepending an rvalue basic_string with the desired allocator:
// use my_favorite_allocator for the final result
my_string(my_favorite_allocator) + meow + woof + cat() + dog();
For better and portable control over allocators, member functions like append, insert, and operator+= should be used on a result string constructed with the desired allocator. π«Ά
The usage ofstd::type_identity_tas parameter in overloads (4), (7), (12), and (16) ensures that an object of typestd::basic_string<CharT, Traits, Allocator>can always be concatenated to an object of a typeTwith an implicit conversion tostd::basic_string_view<CharT, Traits>, and vice versa, as per overload resolution rules. (since C++26)
β Feature-test macro β
__cpp_lib_string_viewβ value202403β Std(C++26)β Feature: Concatenation of strings and string views, overloads (4), (7), (12), (16)
part 1 out, part 2 loadingβ¦ don't go anywhere bestie π
π Example π
okay bestie, this is the full little demo β run it and vibe check the output:
// Run this code
#include <iostream>
#include <string>
#include <string_view>
int main()
{
std::string s1 = "Hello";
std::string s2 = "world";
const char* end = "!\n";
std::cout << s1 + ' ' + s2 + end;
std::string_view water{" Water"};
#if __cpp_lib_string_view >= 202403
std::cout << s1 + water + s2 << end; // overload (4), then (1)
#else
std::cout << s1 + std::string(water) + s2 << end; // OK, but less
efficient
#endif
}
And the output is literally this, no cap:
Hello world!
Hello Waterworld!
π©Ή Defect reports π©Ή
These are the behavior-changing defect reports that got applied retroactively to previously published C++ standards. Very βwe fixed it after the fact, bestieβ energy.
P1165R1β applied to C++11; behavior as published: allocator propagation is haphazard and inconsistent; correct behavior: made more consistent.
π See also π
operator+=β appends characters to the end (public member function)appendβ appends characters to the end (public member function)insertβ inserts characters (public member function)
okay that's the whole page, bestie π
β¨ β source: cppreference.com, version/date: 2024.06.10, for std::operat...asic_string)(3). π
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms