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

### 2386. `function::operator=` handles allocators incorrectly

Section: 22.10.17.3.2 [func.wrap.func.con] Status: NAD Submitter: Pablo Halpern Opened: 2014-05-23 Last modified: 2015-05-05

Priority: 1

View all other issues in [func.wrap.func.con].

View all issues with NAD status.

Discussion:

The Effects clauses for the assignment operator for class template `function` are written as code that constructs a temporary `function` and then swaps it with `*this`. The intention appears to be that assignment should have the strong exception guarantee, i.e., `*this` is not modified if an exception is thrown. However, the current description is incorrect when `*this` was constructed using an allocator.

Part of the problem is the under-specification of `swap`, which does not state the allocator requirements or allocator postconditions. If `swap` behaves like the rest of the standard library, swapping function objects constructed with different allocators would be undefined behavior. Alternatively `swap` could exchange the allocators, though I would argue against this specification.

For either specification of `swap`, the current Effects clauses for `operator=` are incorrect. If `swap` does not exchange the allocators, then `operator=` would have undefined behavior, which is clearly not desired. If `swap` does exchange the allocators, then `operator=` would always leave the left-hand side (lhs) of the assignment with a default allocator. The latter would be surprising behavior, as the allocator instance is normally unchanged for the lifetime of an object (for good reason), and is certainly not reset to default arbitrarily.

The desired behavior is that assignment would leave the allocator of the lhs unchanged. The way to achieve this behavior is to construct the temporary `function` using the original allocator. Unfortunately, we cannot describe the desired behavior in pure code, because there is no way to name the type-erased value of the allocator. (N3916 would improve this situation for the Library Fundamentals TS, but even with those changes, there is no way to recover the original type of the allocator.) The PR below, therefore, uses pseudo-code, inventing a fictitious `ALLOCATOR_OF(f)` expression that evaluates to the actual allocator type, even if that allocator was type erased. I have implemented this PR successfully.

Previous resolution [SUPERSEDED]:

> This wording is relative to N3936.

> 1. Change 22.10.17.3.2 [func.wrap.func.con] as indicated:

>    > In the following descriptions, `ALLOCATOR_OF(f)` is a copy of the allocator specified in the construction of `function` `f`, or `allocator<char>()` if no allocator was specified.

>    >     function& operator=(const function& f);

>    > > -12- Effects: `function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);`

>    > > -13- Returns: `*this`

>    >     function& operator=(function&& f);

>    > > -14- Effects: Replaces the target of `*this` with the target of `f`.`function(allocator_arg, ALLOCATOR_OF(*this), std::move(f)).swap(*this);`

>    > > -15- Returns: `*this`

>    >     function& operator=(nullptr_t);

>    > > -16- Effects: If `*this != nullptr`, destroys the target of `this`.

>    > > -17- Postconditions: `!(*this)`. The allocator is unchanged.

>    > > -18- Returns: `*this`

>    > > -?- Throws: Nothing.

>    >     template<class F> function& operator=(F&& f);

>    > > -19- Effects: `function(allocator_arg, ALLOCATOR_OF(*this), std::forward<F>(f)).swap(*this);`

>    > > -20- Returns: `*this`

>    > > -21- Remarks: This assignment operator shall not participate in overload resolution unless `declval<typename decay<F>::type&>()` is `Callable` (20.9.11.2) for argument types `ArgTypes...` and return type `R`.

>    >     template<class F> function& operator=(reference_wrapper<F> f);

>    > > -22- Effects: `function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);`

>    > > -23- Returns: `*this`

[2015-05, Lenexa]

STL: think this is NAD, don't think this is implementable or even should be.
STL: think this issue should be dealt with the same as 2370, don't think this should be done ever.
STL: NAD because there is nothing broken here.
STL: already fixed `operator= noexcept` so Throws nothing is not needed
STL: nothing to salvage here
MC: consensus for NAD

Proposed resolution:

There was consensus by the committee that the issue does not constitute as defect.