Movatterモバイル変換


[0]ホーム

URL:



This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++17 status.

2063. Contradictory requirements for string move assignment

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].

23.2.2[container.requirements.general] describes move assignment in p7 and Table 99.

Ifallocator_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*this andstr are not the same object, modifies*this as 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.

Additionally assign from an rvaluebasic_string is defined as:

basic_string& assign(basic_string&& str) noexcept;

Effects: The function replaces the string controlled by*this with 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.

  1. 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;    […]  };}
  2. 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*this andstr are not the same object, modifies*this as shown in Table 71. [Note: A valid implementation isswap(str). —end note ]

    -23- If*this andstr are the same object, the member has no effect.

    -24-Returns:*this

    Table 71 —operator=(const basic_string<charT, traits, Allocator>&&)
    ElementValue
    data()points at the array whose first element was pointedat bystr.data()
    size()previous value ofstr.size()
    capacity()a value at least as large assize()
  3. 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*this = std::move(str).The function replaces the string controlled by*this with a string of lengthstr.size() whose elements are a copy of the string controlled bystr. [Note: A valid implementation isswap(str). —end note ]

    -3-Returns:*this

[2012-08-11 Joe Gottman observes:]

One of the effects ofbasic_string's move-assignment operator (27.4.3.3[string.cons], Table 71) is

ElementValue
data()points at the array whose first element was pointed at bystr.data()

If a string implementation uses the small-string optimization and the input stringstr is 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 like

union{   char buffer[SMALL_STRING_SIZE];   char *pdata;};

When the string is small enough to fit insidebuffer, thedata() member function returnsstatic_cast<const char *>(buffer), and sincebuffer is an array variable, there is no way to implement move so that the moved-to string'sbuffer member variable is equal tothis->buffer.

Resolution proposal:

Change Table 71 to read:

ElementValue
data()points at the arraywhose first element was pointed at bystr.data()that contains the same characters in the same order asstr.data() contained beforeoperator=() was called

[2015-05-07, Lenexa]

Howard suggests improved wording

Move to Immediate

Proposed resolution:

This wording is relative to N4431.

  1. 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);    […]  };}
  2. 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:If*this andstr are not the same object, modifies*this as shownin Table 71. [Note: A valid implementation isswap(str). —end note ]Move assigns as a sequence container ([container.requirements]), except that iterators, pointers and references may be invalidated.

    -22- If*this andstr are the same object, the member has no effect.

    -23-Returns:*this

    Table 71 —operator=(basic_string&&) effects
    ElementValue
    data()points at the array whose first element was pointedat bystr.data()
    size()previous value ofstr.size()
    capacity()a value at least as large assize()
  3. 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*this = std::move(str).The function replaces the string controlled by*this with a string of lengthstr.size() whose elements are a copy of the string controlled bystr.[Note: A valid implementation isswap(str). —end note ]

    -4-Returns:*this


[8]ページ先頭

©2009-2026 Movatter.jp