function::operator=
handles allocators incorrectlySection: 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.
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.
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 offunction
f
, orallocator<char>()
if no allocator was specified.function& operator=(const function& f);-12- Effects:
-13- Returns:function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);
*this
function& operator=(function&& f);-14- Effects:
-15- Returns:Replaces the target of*this
with the target off
.function(allocator_arg, ALLOCATOR_OF(*this), std::move(f)).swap(*this);
*this
function& operator=(nullptr_t);-16- Effects: If
-17- Postconditions:*this != nullptr
, destroys the target ofthis
.!(*this)
. The allocator is unchanged. -18- Returns:*this
-?- Throws: Nothing.template<class F> function& operator=(F&& f);-19- Effects:
-20- Returns:function(allocator_arg, ALLOCATOR_OF(*this), std::forward<F>(f)).swap(*this);
*this
-21- Remarks: This assignment operator shall not participate in overload resolution unlessdeclval<typename decay<F>::type&>()
isCallable
(20.9.11.2) for argument typesArgTypes...
and return typeR
.template<class F> function& operator=(reference_wrapper<F> f);-22- Effects:
-23- Returns:function(allocator_arg, ALLOCATOR_OF(*this), f).swap(*this);
*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.