2929. basic_string misuses "Effects: Equivalent to"

Section: 24.3.2.6.3 [string.assign] Status: New Submitter: Jonathan Wakely Opened: 2017-02-03 Last modified: 2017-04-22

Priority: 3

View all other issues in [string.assign].

View all issues with New status.

Discussion:

basic_string::assign(size_type n, charT c); says:

Effects: Equivalent to assign(basic_string(n, c)).

This requires that a new basic_string is constructed, using a default-constructed allocator, potentially allocating memory, and then that new string is copy-assigned to *this, potentially propagating the allocator. This must be done even if this->capacity() > n, because memory allocation and allocator propagation are observable side effects. If the allocator doesn't propagate and isn't equal to this->get_allocator() then a second allocation may be required. This can't be right; it won't even compile if the allocator isn't default constructible.

basic_string::assign(InputIterator first, InputIterator last) has a similar problem, even if the iterators are random access and this->capacity() > distance(first, last).

basic_string::assign(std::initializer_list<charT> doesn't say "Equivalent to" so maybe it's OK to not allocate anything if the list fits in the existing capacity.

basic_string::append(size_type, charT) and basic_string::append(InputIterator, InputIterator) have the same problem, although they don't propagate the allocator, but still require at least one, maybe two allocations.

A partial fix would be to ensure all the temporaries are constructed with get_allocator() so that they don't require default constructible allocators, and so propagation won't alter allocators. The problem of observable side effects is still present (the temporary might need to allocate memory, even if this->capacity() is large) but arguably it's unspecified when construction allocates, to allow for small-string optimisations.

[2017-03-04, Kona]

Set priority to 3. Thomas to argue on reflector that this is NAD.

[2017-03-07, LWG reflector discussion]

Thomas and Jonathan remark that LWG 2788 fixed most cases except the allocator respectance and provide wording for this:

Proposed resolution:

This wording is relative to N4659.

  1. Change 24.3.2.6.3 [string.assign] as indicated:

    basic_string& assign(size_type n, charT c);
    

    -22- Effects: Equivalent to assign(basic_string(n, c, get_allocator())).