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

### 2389. [fund.ts] `function::operator=` is over-specified and handles allocators incorrectly

Section: 4.2.1 [fund.ts::func.wrap.func.con] Status: TS Submitter: Pablo Halpern Opened: 2014-05-23 Last modified: 2017-07-30

Priority: 2

View all issues with TS status.

Discussion:

Addresses: fund.ts

This issue against the TS is similar to LWG 2386, which is against the standard. 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. The description in the standard is incorrect when `*this` was originally constructed using an allocator. The TS attempts to correct the problem, but the correction is incomplete.

The wording in the TS uses `get_memory_resource()` to construct a temporary `function` object with the same allocator as the left-hand size (lhs) of the assignment. The intended result of using this pattern was that the allocator for `*this` would be unchanged, but it doesn't quite work. The problem is that the allocator returned by `get_memory_resource()` is not the same type as the type-erased allocator used to construct the `function` object, but rather a type-erased distillation of that type that is insufficient for making a true copy of the allocator. The rules for type-erased allocators in the TS ([memory.type.erased.allocator]) specify that the lifetime of the object returned by `get_memory_resource()` is sometimes tied to the lifetime of `*this`, which might cause the (single copy of) the allocator to be destroyed if the `swap` operation destroys and reconstructs `*this`, as some implementations do (and are allowed to do).

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 `get_memory_resource()` does not really name the type-erased allocator, as mentioned above. 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.

[2014-06-21, Rapperswil]

Apply to Library Fundamentals TS (after removing the previous "Throws: Nothing" element to prevent an editorial conflict with 2401).

Proposed resolution:

This wording is relative to N3908.

1. Change in [mods.func.wrap] in the Library TS as indicated:

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

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

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

   > > […]

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

   > > -8- Effects: `function(allocator_arg, get_memory_resource()ALLOCATOR_OF(*this), std::move(f)).swap(*this);`

   > > […]

   >     function& operator=(nullptr_t);

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

   > > -12- Postconditions: `!(*this)`. The memory resource returned by `get_memory_resource()` after the assignment is equivalent to the memory resource before the assignment. [Note: the address returned by `get_memory_resource()` might change — end note]

   > > -13- Returns: `*this`

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

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

   > > […]

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

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

   > > […]