This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 2705. Questionable precondition on Sequence containers `a.assign(n, t)`

Section: 24.2.4 [sequence.reqmts] Status: New Submitter: Kazutoshi Satoda Opened: 2016-05-08 Last modified: 2020-09-06

Priority: 3

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with New status.

Discussion:

Please look through the following modifications on a value `v` of type `vector<T>`:

>     assert(v.size() > 0);
>     v.push_back(v[0]);
>     v.insert(v.begin(), v[0]);
>     v.resize(v.size() * 2, v[0]);
>     v.assign(v.size() * 2, v[0]);

All of these use an element of itself which may be moved or destroyed by the modification.

From what I see so far, the first three are required to work. Please see library issue 526 for validity of them.

But only the last one is undefined because it violates a precondition of a sequence container operation. I think this is too subtle.

Should it be like that, really?

The precondition is in Table 107 "Sequence container requirements" at the next of 24.2.4 [sequence.reqmts] p3.

> In Tables 107 and 108, `X` denotes a sequence container class, `a` denotes a value of `X` containing elements of type `T`, […] `n` denotes a value of `X::size_type`, […] `t` denotes an lvalue or a `const` rvalue of `X::value_type`, […]

> […]

> Table 107 — Sequence container requirements (in addition to container)
> Expression | Return type | Assertion/note pre-/post-condition
> --- | --- | ---
> `[…]`
> `a.assign(n, t)` | `void` | Requires: `T` shall be `CopyInsertable` into `X` and `CopyAssignable`. pre: `t` is not a reference into `a`. Replaces elements in `a` with `n` copies of `t`.

I looked into the following implementations:

- libc++ relies on the precondition.

  It deallocates first on `n > capacity()` case, see here.

- libstdc++ doesn't rely on the precondition.

  It creates temporary `vector(n, t)` and `swap()` on `n > capacity()` case, see here.

- MSVC relies on the precondition.

  It unconditionally does `clear()` and then `insert(begin(), n, t)`. I looked into my local "`%PROGRAMFILES(X86)%/Microsoft Visual Studio 14.0/VC/include/vector`".

One drawback of libstdc++ implementation, I could find so far, is possibly increased peek memory usage (both old and new buffer exist at the same time). But, because the same can happen on the most other modifications, it seems a reasonable trade-off to remove the precondition to fill the subtle gap. Users who really needs less memory usage can do `clear()` and `insert()` by themselves.

I also found that `basic_string::assign(n, c)` is safe on this point. At 23.4.3.7.3 [string.assign] p17:

>     basic_string& assign(size_type n, charT c);

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

> > Returns: `*this`.

This can be seen as another gap.

Looking back on the history, I found that the definition of `assign(n, t)` was changed at C++14 for library issue 2209. There were more restricting definitions like this:

>     void assign(size_type n, const T& t);

> > Effects:

> > >     erase(begin(), end());
> > >     insert(begin(), n, t);

I think the precondition was probably set to accept this old definition and is not required inherently. And if the less memory usage was really intended, the standard is now underspecifying about that.

[2016-05 Issues Telecon]

Howard believes this should be NAD, but we tabled the discussion.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4582.

> 1. In 24.2.4 [sequence.reqmts], edit Table 107 (Sequence container requirements) as indicated:

>    > Table 107 — Sequence container requirements (in addition to container)
>    > Expression | Return type | Assertion/note pre-/post-condition
>    > --- | --- | ---
>    > `[…]`
>    > `a.assign(n, t)` | `void` | Requires: `T` shall be `CopyInsertable` into `X` and `CopyAssignable`. pre: `t` is not a reference into `a`. Replaces elements in `a` with `n` copies of `t`.

[2020-04-25, Daniel syncs current wording with recent working draft]

Proposed resolution:

This wording is relative to N4861.

1. In 24.2.4 [sequence.reqmts], edit Table [tab:container.seq.req] (Sequence container requirements) as indicated:

   > Table 77 — Sequence container requirements (in addition to container) [tab:container.seq.req]
   > Expression | Return type | Assertion/note pre-/post-condition
   > --- | --- | ---
   > `[…]`
   > `a.assign(n, t)` | `void` | Preconditions: `T` is Cpp17CopyInsertable into `X` and Cpp17CopyAssignable. `t` is not a reference into `a`. Effects: Replaces elements in `a` with `n` copies of `t`. Invalidates all references, pointers and iterators referring to the elements of `a`. For `vector` and `deque`, also invalidates the past-the-end iterator.