This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++17 status.
Section: 27.4.3[basic.string]Status:C++17Submitter: Howard HinnantOpened: 2011-05-29Last modified: 2017-07-30
Priority:3
View otheractive issues in [basic.string].
View all otherissues in [basic.string].
View all issues withC++17 status.
Discussion:
27.4.3.2[string.require]/p4 says thatbasic_string is an "allocator-aware" container and behaves as described in 23.2.2[container.requirements.general].
allocator_traits<allocator_type>::propagate_on_container_move_assignment::value is false, and if the allocators stored in the lhs and rhs sides are not equal, then move assigning a string has the same semantics as copy assigning a string as far as resources are concerned (resources can not be transferred). And in this event, the lhs may have to acquire resources to gain sufficient capacity to store a copy of the rhs.However 27.4.3.3[string.cons]/p22 says:basic_string<charT,traits,Allocator>&operator=(basic_string<charT,traits,Allocator>&& str) noexcept;Effects: If
*thisandstrare not the same object, modifies*thisas shown in Table 71. [Note: A valid implementation isswap(str). —end note ]
These two specifications forbasic_string::operator=(basic_string&&) are in conflict with each other. It is not possible to implement abasic_string which satisfies both requirements.
basic_string is defined as:basic_string& assign(basic_string&& str) noexcept;Effects: The function replaces the string controlled by
*thiswith a string of lengthstr.size()whose elements are a copy of the string controlled bystr. [Note: A valid implementation isswap(str). —end note ]
It seems contradictory that this member can be sensitive topropagate_on_container_swap instead ofpropagate_on_container_move_assignment. Indeed, there is a very subtle chance for undefined behavior here: If the implementation implements this in terms ofswap, and ifpropagate_on_container_swap is false, and if the two allocators are unequal, the behavior is undefined, and will likely lead to memory corruption. That's a lot to go wrong under a member named "assign".
[2011 Bloomington]
Alisdair: Can this be conditionalnoexcept?
Pablo: We said we were not going to put in many conditionalnoexcepts. Problem is not allocator, but non-normative definition. It says swap is a valid operation which it is not.
Dave: Move assignment is not a critical method.
Alisdair: Was confusing assignment and construction.
Dave: Move construction is critical for efficiency.
Kyle: Is it possible to test fornoexcept.
Alisdair: Yes, query thenoexcept operator.
Alisdair: Agreed there is a problem that we cannot unconditionally mark these operations asnoexcept.
Pablo: How come swap is not defined in alloc
Alisdair: It is in utility.
Pablo: Swap has a conditionalnoexcept. Is no throw move constructable, is no throw move assignable.
Pablo: Not critical for strings or containers.
Kyle: Why?
Pablo: They do not use the default swap.
Dave: Important for deduction in other types.
Alisdair: Would change the policy we adopted during FDIS mode.
Pablo: Keep it simple and get some vendor experience.
Alisdair: Is this wording correct? Concerned with bullet 2.
Pablo: Where does it reference containers section.
Alisdair: String is a container.
Alisdair: We should not remove redundancy piecemeal.
Pablo: I agree. This is a deviation from rest of string. Missing forward reference to containers section.
Pablo: To fix section 2. Only the note needs to be removed. The rest needs to be a forward reference to containers.
Alisdair: That is a new issue.
Pablo: Not really. Talking about adding one sentence, saying that basic string is a container.
Dave: That is not just a forward reference, it is a semantic change.
PJ: We intended to make it look like a container, but it did not satisfy all the requirements.
Pablo: Clause 1 is correct. Clause 2 is removing note andnoexcept (do not remove the rest). Clause 3 is correct.
Alisdair: Not sure data() is correct (in clause 2).
Conclusion: Move to open, Alisdair and Pablo volunteered to provide wording
[originally proposed wording:]
This wording is relative to the FDIS.
Modify the class templatebasic_string synopsis in 27.4.3[basic.string]:
namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { public: […] basic_string& operator=(basic_string&& str)noexcept; […] basic_string& assign(basic_string&& str)noexcept; […] };}Remove the definition of thebasic_string move assignment operator from 27.4.3.3[string.cons] entirely, including Table 71 —operator=(const basic_string<charT, traits, Allocator>&&).This is consistent with how we define move assignment for the containers in Clause 23:
basic_string<charT,traits,Allocator>&operator=(basic_string<charT,traits,Allocator>&& str) noexcept;
-22-Effects: If*thisandstrare not the same object, modifies*thisas shown in Table 71. [Note: A valid implementation isswap(str). —end note ]-23- If*thisandstrare the same object, the member has no effect.-24-Returns:*this
Table 71 —operator=(const basic_string<charT, traits, Allocator>&&)ElementValuedata()points at the array whose first element was pointedat bystr.data()size()previous value ofstr.size()capacity()a value at least as large assize()
Modify the paragraphs prior to 27.4.3.7.3[string.assign] p.3 as indicated (Thefirst insertion recommends a separate paragraph number for the indicated paragraph):
basic_string& assign(basic_string&& str)noexcept;-?-Effects:Equivalent to
-3-Returns:*this = std::move(str).The function replaces the string controlled by*thiswith a string of lengthstr.size()whose elements are a copy of the string controlled bystr. [Note: A valid implementation isswap(str). —end note ]*this
[2012-08-11 Joe Gottman observes:]
One of the effects of
basic_string's move-assignment operator (27.4.3.3[string.cons], Table 71) is
Element Value data()points at the array whose first element was pointed at by str.data()If a string implementation uses the small-string optimization and the input string
stris small enough to make use of it, this effect is impossible to achieve. To use the small string optimization, a string has to be implemented using something likeunion{ char buffer[SMALL_STRING_SIZE]; char *pdata;};When the string is small enough to fit inside
Resolution proposal:Change Table 71 to read:buffer, thedata()member function returnsstatic_cast<const char *>(buffer), and sincebufferis an array variable, there is no way to implement move so that the moved-to string'sbuffermember variable is equal tothis->buffer.
Element Value data()points at the array whose first element was pointed at bythat contains the same characters in the same order asstr.data()str.data()contained beforeoperator=()was called
[2015-05-07, Lenexa]
Howard suggests improved wording
Move to ImmediateProposed resolution:
This wording is relative to N4431.
Modify the class templatebasic_string synopsis in 27.4.3[basic.string]:
namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { public: […] basic_string& assign(basic_string&& str) noexcept( allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); […] };}Change 27.4.3.3[string.cons]/p21-23:
basic_string&operator=(basic_string&& str) noexcept( allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value);-21-Effects:
IfMove assigns as a sequence container ([container.requirements]), except that iterators, pointers and references may be invalidated.*thisandstrare not the same object, modifies*thisas shownin Table 71. [Note: A valid implementation isswap(str). —end note ]-22- If-23-Returns:*thisandstrare the same object, the member has no effect.*this
Table 71 —operator=(basic_string&&)effectsElementValuedata()points at the array whose first element was pointedat bystr.data()size()previous value ofstr.size()capacity()a value at least as large assize()
Modify the paragraphs prior to 27.4.3.7.3[string.assign] p.3 as indicated
basic_string& assign(basic_string&& str) noexcept( allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value);-3-Effects:Equivalent to
-4-Returns:*this = std::move(str).The function replaces the string controlled by*thiswith a string of lengthstr.size()whose elements are a copy of the string controlled bystr.[Note: A valid implementation isswap(str). —end note ]*this