Difference between revisions of "cpp/string/basic string/operator+"
(Created page with "{{cpp/string/basic_string/title | operator+}} {{cpp/string/basic_string/sidebar}} {{ddcl list begin}} {{ddcl list item | num=1 | 1= template<class charT, class traits, class Allo...") |
m (→Synopsis: fmt: rm indents below <template>) |
||
(27 intermediate revisions by 9 users not shown) | |||
Line 1: | Line 1: | ||
− | {{cpp | + | {{cpp/title|operator+{{small|(std::basic_string)}}}} |
− | {{cpp/string/basic_string/ | + | {{cpp/string/basic_string/navbar}} |
− | {{ | + | {{dcl begin}} |
− | {{ | + | {{dcl header|string}} |
− | template<class | + | {{dcla|num=1|notes={{mark constexpr since c++20}}|1= |
− | + | template< class CharT, class Traits, class Alloc > | |
− | + | std::basic_string<CharT,Traits,Alloc> | |
− | + | operator+( const std::basic_string<CharT,Traits,Alloc>& lhs, | |
+ | const std::basic_string<CharT,Traits,Alloc>& rhs ); | ||
}} | }} | ||
− | {{ | + | {{dcl|num=2|notes={{mark constexpr since c++20}}|1= |
− | template<class | + | template< class CharT, class Traits, class Alloc > |
− | + | std::basic_string<CharT,Traits,Alloc> | |
− | + | operator+( const std::basic_string<CharT,Traits,Alloc>& lhs, | |
− | + | const CharT* rhs ); | |
}} | }} | ||
− | {{ | + | {{dcl|num=3|notes={{mark constexpr since c++20}}|1= |
− | template<class | + | template< class CharT, class Traits, class Alloc > |
− | + | std::basic_string<CharT,Traits,Alloc> | |
− | + | operator+( const std::basic_string<CharT,Traits,Alloc>& lhs, | |
− | + | CharT rhs ); | |
}} | }} | ||
− | {{ | + | {{dcla|num=4|since=c++26|1= |
− | template<class | + | template< class CharT, class Traits, class Alloc > |
− | + | 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 ); | |
}} | }} | ||
− | {{ | + | {{dcl|num=5|notes={{mark constexpr since c++20}}|1= |
− | template<class | + | template< class CharT, class Traits, class Alloc > |
− | + | std::basic_string<CharT,Traits,Alloc> | |
− | + | operator+( const CharT* lhs, | |
− | + | const std::basic_string<CharT,Traits,Alloc>& rhs ); | |
}} | }} | ||
− | {{ | + | {{dcl|num=6|notes={{mark constexpr since c++20}}|1= |
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( CharT lhs, | ||
+ | const std::basic_string<CharT,Traits,Alloc>& rhs ); | ||
+ | }} | ||
+ | {{dcla|num=7|since=c++26|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | 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 ); | ||
+ | }} | ||
+ | {{dcla|num=8|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( std::basic_string<CharT,Traits,Alloc>&& lhs, | ||
+ | std::basic_string<CharT,Traits,Alloc>&& rhs ); | ||
+ | }} | ||
+ | {{dcl|num=9|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( std::basic_string<CharT,Traits,Alloc>&& lhs, | ||
+ | const std::basic_string<CharT,Traits,Alloc>& rhs ); | ||
+ | }} | ||
+ | {{dcl|num=10|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( std::basic_string<CharT,Traits,Alloc>&& lhs, | ||
+ | const CharT* rhs ); | ||
+ | }} | ||
+ | {{dcl|num=11|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( std::basic_string<CharT,Traits,Alloc>&& lhs, | ||
+ | CharT rhs ); | ||
+ | }} | ||
+ | {{dcla|num=12|since=c++26|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | 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 ); | ||
+ | }} | ||
+ | {{dcl|num=13|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( const std::basic_string<CharT,Traits,Alloc>& lhs, | ||
+ | std::basic_string<CharT,Traits,Alloc>&& rhs ); | ||
+ | }} | ||
+ | {{dcl|num=14|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( const CharT* lhs, | ||
+ | std::basic_string<CharT,Traits,Alloc>&& rhs ); | ||
+ | }} | ||
+ | {{dcl|num=15|since=c++11|notes={{mark constexpr since c++20}}|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | std::basic_string<CharT,Traits,Alloc> | ||
+ | operator+( CharT lhs, | ||
+ | std::basic_string<CharT,Traits,Alloc>&& rhs ); | ||
+ | }} | ||
+ | {{dcla|num=16|since=c++26|1= | ||
+ | template< class CharT, class Traits, class Alloc > | ||
+ | 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 ); | ||
+ | }} | ||
+ | {{dcl end}} | ||
+ | |||
+ | Returns a string containing characters from {{c|lhs}} followed by the characters from {{c|rhs}}. Equivalent to: | ||
+ | |||
+ | @1,2@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = lhs; r.append(rhs); return r;}} | ||
+ | @3@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = lhs; r.push_back(rhs); return r;}} | ||
+ | @4@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = lhs; r.append(rhs); return r;}} | ||
+ | @5@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(0, lhs); return r;}} | ||
+ | @6@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(r.begin(), lhs); return r;}} | ||
+ | @7@ {{c|1=std::basic_string<CharT, Traits, Allocator> r = rhs; r.insert(0, lhs); return r;}} | ||
+ | @8@ {{c|1=lhs.append(rhs); return std::move(lhs);}} except that both {{c|lhs}} and {{c|rhs}} are left in valid but unspecified states. If {{c|lhs}} and {{c|rhs}} have equal allocators, the implementation can move from either. | ||
+ | @9,10@ {{c|1=lhs.append(rhs); return std::move(lhs);}} | ||
+ | @11@ {{c|1=lhs.push_back(rhs); return std::move(lhs);}} | ||
+ | @12@ {{c|1=lhs.append(rhs); return std::move(lhs);}} | ||
+ | @13,14@ {{c|1=rhs.insert(0, lhs); return std::move(rhs);}} | ||
+ | @15@ {{c|1=rhs.insert(rhs.begin(), lhs); return std::move(rhs);}} | ||
+ | @16@ {{c|1=rhs.insert(0, lhs); return std::move(rhs);}} | ||
+ | |||
+ | {{rrev|since=c++11| | ||
+ | The allocator used for the result is: | ||
+ | @1-4@ {{c|std::allocator_traits<Alloc>::select_on_container_copy_construction(lhs.get_allocator()) | ||
+ | }} | ||
+ | @5-7@ {{c|std::allocator_traits<Alloc>::select_on_container_copy_construction(rhs.get_allocator()) | ||
+ | }} | ||
+ | @8-12@ {{c|lhs.get_allocator()}} | ||
+ | @13-16@ {{c|rhs.get_allocator()}} | ||
+ | |||
+ | In other words: | ||
+ | * If one operand is a {{tt|basic_string}} rvalue, its allocator is used. | ||
+ | * Otherwise, {{tt|select_on_container_copy_construction}} is used on the allocator of the lvalue {{tt|basic_string}} operand. | ||
+ | In each case, the left operand is preferred when both are {{tt|basic_string}}s of the same value category. | ||
+ | |||
+ | For {{vl|8-16}}, all rvalue {{tt|basic_string}} operands are left in valid but unspecified states. | ||
+ | }} | ||
+ | |||
+ | ===Parameters=== | ||
+ | {{par begin}} | ||
+ | {{par|lhs|string{{rev inl|since=c++26|, string view}}, character, or pointer to the first character in a null-terminated array}} | ||
+ | {{par|rhs|string{{rev inl|since=c++26|, string view}}, character, or pointer to the first character in a null-terminated array}} | ||
+ | {{par end}} | ||
+ | |||
+ | ===Return value=== | ||
+ | A string containing characters from {{c|lhs}} followed by the characters from {{c|rhs}}{{rev inl|since=c++11|, using the allocator determined as above}}. | ||
+ | |||
+ | {{rrev|since=c++11|1= | ||
+ | ===Notes=== | ||
+ | {{tt|operator+}} should be used with great caution when stateful allocators are involved{{rev inl|since=c++17| (such as when {{lc|std::pmr::string}} is used)}}. Prior to {{wg21|P1165R1}}, the allocator used for the result was determined by historical accident and can vary from overload to overload for no apparent reason. Moreover, for {{vl|1-5}}, the allocator propagation behavior varies across major standard library implementations and differs from the behavior depicted in the standard. | ||
+ | |||
+ | Because the allocator used by the result of {{tt|operator+}} is sensitive to value category, {{tt|operator+}} is not associative with respect to allocator propagation: | ||
+ | |||
+ | {{source|1= | ||
+ | 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 | ||
+ | |||
+ | 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 {{tt|operator+}} invocations, the allocator used for the ultimate result may be controlled by prepending an rvalue {{tt|basic_string}} with the desired allocator: | ||
+ | |||
+ | {{source|1= | ||
+ | // 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 {{rlpt|append}}, {{rlpt|insert}}, and {{rlpt|1=operator+=}} should be used on a result string constructed with the desired allocator. | ||
+ | }} | ||
+ | |||
+ | {{rrev|since=c++26|1= | ||
+ | The usage of {{c/core|std::type_identity_t}} as parameter in overloads {{vl|4}}, {{vl|7}}, {{vl|12}}, and {{vl|16}} ensures that an object of type {{c/core|std::basic_string<CharT, Traits, Allocator>}} can always be concatenated to an object of a type {{tt|T}} with an implicit conversion to {{c/core|std::basic_string_view<CharT, Traits>}}, and vice versa, as per [[cpp/language/overload_resolution#Call to an overloaded operator|overload resolution]] rules<!--[over.match.oper]-->. | ||
+ | |||
+ | {{ftm begin}} | ||
+ | {{ftm|__cpp_lib_string_view|std=C++26|value=202403|Concatenation of strings and string views, overloads {{vl|4}}, {{vl|7}}, {{vl|12}}, {{vl|16}}}} | ||
+ | {{ftm end}} | ||
+ | }} | ||
+ | |||
+ | ===Example=== | ||
+ | {{example | ||
+ | |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 | ||
+ | } | ||
+ | |output= | ||
+ | Hello world! | ||
+ | Hello Waterworld! | ||
+ | }} | ||
+ | |||
+ | ===Defect reports=== | ||
+ | {{dr list begin}} | ||
+ | {{dr list item|paper=P1165R1|std=C++11|before=allocator propagation is haphazard and inconsistent|after=made more consistent}} | ||
+ | {{dr list end}} | ||
− | + | ===See also=== | |
+ | {{dsc begin}} | ||
+ | {{dsc inc|cpp/string/basic_string/dsc operator+{{=}}}} | ||
+ | {{dsc inc|cpp/string/basic_string/dsc append}} | ||
+ | {{dsc inc|cpp/string/basic_string/dsc insert}} | ||
+ | {{dsc end}} | ||
− | {{ | + | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 16:26, 12 May 2024
Defined in header <string>
|
||
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(1) | (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(2) | (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(3) | (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > constexpr std::basic_string<CharT,Traits,Alloc> |
(4) | (since C++26) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(5) | (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(6) | (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > constexpr std::basic_string<CharT,Traits,Alloc> |
(7) | (since C++26) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(8) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(9) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(10) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(11) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > constexpr std::basic_string<CharT,Traits,Alloc> |
(12) | (since C++26) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(13) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(14) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > std::basic_string<CharT,Traits,Alloc> |
(15) | (since C++11) (constexpr since C++20) |
template< class CharT, class Traits, class Alloc > constexpr std::basic_string<CharT,Traits,Alloc> |
(16) | (since C++26) |
Returns a string containing characters from lhs followed by the characters from rhs. Equivalent to:
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:
In each case, the left operand is preferred when both are For (8-16), all rvalue |
(since C++11) |
Contents |
[edit] 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 |
[edit] Return value
A string containing characters from lhs followed by the characters from rhs, using the allocator determined as above(since C++11).
Notes
Because the allocator used by the result of 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 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 // 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 |
(since C++11) |
The usage of std::type_identity_t as parameter in overloads (4), (7), (12), and (16) ensures that an object of type std::basic_string<CharT, Traits, Allocator> can always be concatenated to an object of a type
|
(since C++26) |
[edit] Example
#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 }
Output:
Hello world! Hello Waterworld!
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
P1165R1 | C++11 | allocator propagation is haphazard and inconsistent | made more consistent |
[edit] See also
appends characters to the end (public member function) | |
appends characters to the end (public member function) | |
inserts characters (public member function) |