std::function
swapsSection: 22.10.17.3 [func.wrap.func], 22.10.17.3.3 [func.wrap.func.mod] Status: C++17 Submitter: Daniel Krügler Opened: 2011-05-28 Last modified: 2020-09-06
Priority: 2
View all other issues in [func.wrap.func].
View all issues with C++17 status.
Discussion:
Howard Hinnant observed in reflector message c++std-lib-30841 that 22.10.17.3 [func.wrap.func]
makes the member swap noexcept
, even though the non-member swap is not noexcept
.
noexcept
specifier at the member swap is incorrect and should be removed.
But if we allow for a potentially throwing member swap of std::function
, this causes
another conflict with the exception specification for the following member function:
template<class F> function& operator=(reference_wrapper<F> f) noexcept;
Effects:
function(f).swap(*this);
Note that in this example the sub-expression function(f)
does not cause any problems,
because of the nothrow-guarantee given in 22.10.17.3.2 [func.wrap.func.con] p. 10. The problem
is located in the usage of the swap which could potentially throw given the general latitude.
std::function
should be noexcept), or this function needs to be adapted as well,
e.g. by taking the exception-specification away or by changing the semantics.
One argument for "swap-may-throw" would be to allow for small-object optimization techniques
where the copy of the target may throw. But given the fact that the swap function has been guaranteed
to be "Throws: Nothing" from TR1 on, it seems to me that that there would still be opportunities to
perform small-object optimizations just restricted to the set of target copies that cannot throw.
In my opinion member swap of std::function
has always been intended to be no-throw, because
otherwise there would be no good technical reason to specify the effects of several member
functions in terms of the "construct-swap" idiom (There are three functions that are defined
this way), which provides the strong exception safety in this case. I suggest to enforce that both
member swap and non-member swap of std::function
are nothrow functions as it had been guaranteed
since TR1 on.
[ 2011 Bloomington ]
Dietmar: May not be swappable in the first place.
Alisdair: This is wide contact. Then we should be taking noexcept off instead of putting it on. This is preferred resolution.
Pablo: This is bigger issue. Specification of assignment in terms of swap is suspect to begin with. It is over specification. How this was applied to string is a better example to work from.
Pablo: Two problems: inconsistency that should be fixed (neither should have noexcept), the other issues is that assignment should not be specified in terms of swap. There are cases where assignment should succeed where swap would fail. This is easier with string as it should follow container rules.
Action Item (Alisdair): There are a few more issues found to file.
Dave: This is because of allocators? The allocator makes this not work.
Howard: There is a type erased allocator in shared_ptr. There is a noexcept allocator in shared_ptr.
Pablo: shared_ptr is a different case. There are shared semantics and the allocator does move around. A function does not have shared semantics.
Alisdair: Function objects think they have unique ownership.
Howard: In function we specify semantics with copy construction and swap.
Action Item (Pablo): Write this up better (why assignment should not be defined in terms of swap)
Howard: Not having trouble making function constructor no throw.
Dietmar: Function must allocate memory.
Howard: Does not put stuff that will throw on copy or swap in small object optimization. Put those on heap. Storing allocator, but has to be no throw copy constructable.
Pablo: Are you allowed to or required to swap or move allocators in case or swap or move.
Dave: An allocator that is type erased should be different...
Pablo: it is
Dave: Do you need to know something about allocator types? But only at construction time.
Pablo: You could have allocators that are different types.
Dave: Swap is two ended operation.
Pablo: Opinion is that both have to say propagate on swap for them to swap.
John: It is not arbitrary. If one person says no. No is no.
Howard: Find noexcept swap to be very useful. Would like to move in that direction and bring container design along.
Dave: If you have something were allocator must not propagate you can detect that at construction time.
...
Pablo: Need to leave this open and discuss in smaller group.
Alisdair: Tried to add boost::any as TR2 proposal and ran into this issue. Only the first place where we run into issues with type erased allocators. Suggest we move it to open.
Action Item: Move to open.
Action Item (Pablo works with Howard and Daniel): Address the more fundamental issue (which may be multiple issues) and write up findings.
Previous resolution [SUPERSEDED]:
This wording is relative to the FDIS.
Modify the header
<functional>
synopsis in 22.10 [function.objects] as indicated:namespace std { […] template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }Modify the class template
function
synopsis in 22.10.17.3 [func.wrap.func] as indicated:namespace std { […] // [func.wrap.func.alg], specialized algorithms: template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }Modify 22.10.17.3.8 [func.wrap.func.alg] as indicated:
template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>& f1, function<R(ArgTypes...)>& f2) noexcept;-1- Effects:
f1.swap(f2);
[2014-02-28 (Post Issaquah), Pablo provides more information]
For cross-referencing purposes: The resolution of this issue should be
harmonized with any resolution to LWG 2370, which addresses
inappropriate noexcept
s in some function constructors.
We have the following choices:
swap()
does not throw
Discussion: This definition is desirable, and allows assignment to be implemented with the strong exception guarantee, but it does have consequences: The implementation cannot use the small-object optimization for a function-object
F
unlessF
isNothrowMovable
(nothrow-swappable is unimportant becauseF
is not swapped with anotherF
). Note that many functors written before C++11 will not have move constructors decorated withnoexcept
, so this limitation could affect a lot of code.It is not clear what other implementation restrictions might be needed. Allocators are required not to throw on move or copy. Is that sufficient?
swap()
can throw
Discussion: This definition gives maximum latitude to implementation to use small-object optimization. However, the strong guarantee on assignment is difficult to achieve. Should we consider giving up on the strong guarantee? How much are we willing to pessimize code for exceptions?
swap()
will not throw if both functions have NoThrowMoveable
functors
Discussion: This definition is similar to option 2, but gives slightly stronger guarantees. Here,
swap()
can throw, but the programmer can theoretically prevent that from happening. This should be straight-forward to implement and gives the implementation a lot of latitude for optimization. However, because this is a dynamic decision, the program is not as easy to reason about. Also, the strong guarantee for assignment is compromized as in option 2.
[2016-08-02, Ville, Billy, and Billy comment and reinstantiate the original P/R]
We (Ville, Billy, and Billy) propose to require that function
's swap
is noexcept
in all cases.
libstdc++ does not throw in their swap
. It is not noexcept
today, but the small functor
optimization only engages for trivially copyable types.
msvc++ checks is_nothrow_move_constructible
before engaging the small functor optimization and marks
its swap
noexcept
libc++ marks swap
noexcept
(though I have not looked at its implementation)
Moreover, many of the concerns that were raised by providing this guarantee are no longer applicable now that
P0302 has been accepted, which removes allocator support from std::function
.
[2016-08 Chicago]
Tues PM: Move to Tentatively Ready
Proposed resolution:
This wording is relative to N4606.
Modify the header <functional>
synopsis in 22.10 [function.objects] as indicated:
namespace std { […] template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }
Modify the class template function
synopsis in 22.10.17.3 [func.wrap.func] as indicated:
namespace std { […] // [func.wrap.func.alg], specialized algorithms: template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }
Modify 22.10.17.3.8 [func.wrap.func.alg] as indicated:
template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>& f1, function<R(ArgTypes...)>& f2) noexcept;-1- Effects: As if by:
f1.swap(f2);