2386. function::operator= handles allocators incorrectly

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

Priority: 1

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

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 23.14.13.2.1 [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.