This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofResolved status.
noexcept instd::functionSection: 22.10.17.3[func.wrap.func]Status:ResolvedSubmitter: Pablo HalpernOpened: 2014-02-27Last modified: 2020-09-06
Priority:3
View all otherissues in [func.wrap.func].
View all issues withResolved status.
Discussion:
The following constructors in 22.10.17.3[func.wrap.func] are declarednoexcept, eventhough it is not possible for an implementation to guarantee that they will not throw:
template <class A> function(allocator_arg_t, const A&) noexcept;template <class A> function(allocator_arg_t, const A&, nullptr_t) noexcept;
In addition, the following functions are guaranteed not to throw if the targetis a function pointer or areference_wrapper:
template <class A> function(allocator_arg_t, const A& a, const function& f);template <class F, class A> function(allocator_arg_t, const A& a, F f);
In all of the above cases, the function object might need to allocate memory(an operation that can throw) in order to hold a copy of the type-erasedallocator itself. The first two constructors produce an empty functionobject, but the allocator is still needed in case the object is later assignedto. In this case, we note that the propagation of allocators on assignment isunderspecified forstd::function. There are three possibilities:
The allocator is never copied on copy-assignment, moved on move-assignment, or swapped on swap.
The allocator is always copied on copy-assignment, moved on move-assignment, and swapped on swap.
Whether or not the allocator is copied, moved, or swapped is determined at run-time based on thepropagate_on_container_copy_assignment andpropagate_on_container_move_assignment traits of the allocators at construction of the source function, the target function, or both.
Although the third option seems to be the most consistent with existingwording in the containers section of the standard, it is problematic in anumber of respects. To begin with, the propagation behavior is determined atrun time based on a pair of type-erased allocators, instead of at compiletime. Such run-time logic isnot consistent with the rest of the standardand is hard to reason about. Additionally, there are two allocator typesinvolved, rather than one. Any set of rules that attempts to rationallyinterpret the propagation traits of both allocators is likely to be arcaneat best, and subtly wrong for some set of codes at worst.
The second option is a non-starter. Historically, and in the vast majority ofexisting code, an allocator does not change after an object is constructed.The second option, if adopted, would undermine the programmer's ability toconstruct, e.g., an array of function objects, all using the same allocator.
The first option is (in Pablo's opinion) the simplest and best. It isconsistent with historical use of allocators, is easy to understand, andrequires minimal wording. It is also consistent with the wording in N3916,which formalizes type-erased allocators.
For cross-referencing purposes: The resolution of this issue should beharmonized with any resolution to LWG2062(i), which questions thenoexceptspecification on the following member functions of std::function:
template <class F> function& operator=(reference_wrapper<F>) noexcept;void swap(function&) noexcept;
[2015-05 Lenexa]
MC: change to P3 and status to open.
STL: note thatnoexcept is an issue and large chunks of allocator should be destroyed.[2015-12-16, Daniel comments]
See2564(i) for a corresponding issue addressing library fundamentals v2.
Previous resolution [SUPERSEDED]:
This wording is relative to N3936.
Change 22.10.17.3[func.wrap.func], class template
functionsynopsis, as indicated:template <class A> function(allocator_arg_t, const A&)noexcept;template <class A> function(allocator_arg_t, const A&, nullptr_t)noexcept;Change 22.10.17.3.2[func.wrap.func.con] as indicated:
-1- When any function constructor that takes a first argument of type
allocator_arg_tis invoked, the secondargument shall have a type that conforms to the requirements forAllocator(Table 17.6.3.5). A copy of theallocator argument is used to allocate memory, if necessary, for the internal data structures of the constructedfunction object. For the remaining constructors, an instance ofallocator<T>, for some suitable typeT, is used to allocate memory, if necessary, for the internal data structures of the constructed function object.function() noexcept;template <class A> function(allocator_arg_t, const A&)noexcept;-2-Postconditions:
!*this.function(nullptr_t) noexcept;template <class A> function(allocator_arg_t, const A&, nullptr_t)noexcept;-3-Postconditions:
!*this.function(const function& f);template <class A> function(allocator_arg_t, const A& a, const function& f);-4-Postconditions:
!*thisif!f; otherwise,*thistargets a copy off.target().-5-Throws: shall not throw exceptions if
f's target is a callable object passedviareference_wrapperor a function pointer. Otherwise, may throwbad_allocor any exception thrown by the copy constructor of the stored callableobject. [Note: Implementations are encouraged to avoid the use ofdynamically allocated memory for small callable objects, for example, wheref's target is an object holding only a pointer or reference to an object anda member function pointer. —end note]template <class A> function(allocator_arg_t, const A& a, const function& f);-?-Postconditions:
!*thisif!f; otherwise,*thistargets a copy off.target().function(function&& f);template <class A> function(allocator_arg_t, const A& a, function&& f);-6-Effects: If
!f,*thishas no target; otherwise, move-constructs the targetoffinto the target of*this, leavingfin a valid state with anunspecified value.If an allocator is not specified, the constructed function will use the same allocator asf.template<class F> function(F f);template <class F, class A> function(allocator_arg_t, const A& a, F f);-7-Requires:
-8-Remarks: These constructors shall not participate in overload resolution unlessFshall beCopyConstructible.fis Callable (20.9.11.2)for argument typesArgTypes...and return typeR.-9-Postconditions:
!*thisif any of the following hold:
fis a null function pointer value.
fis a null member pointer value.
Fis an instance of the function class template, and!f-10- Otherwise,
*thistargets a copy offinitialized withstd::move(f).[Note: Implementations are encouraged to avoid the use of dynamicallyallocated memory for small callable objects, for example, wheref's targetis an object holding only a pointer or reference to an object and a memberfunction pointer. —end note]-11-Throws: shall not throw exceptions whenan allocator is not specifiedand
fis a function pointer or areference_wrapper<T>for someT. Otherwise, may throwbad_allocor any exception thrown byF's copy ormove constructor or byA's allocate function.
[2016-08 Chicago]
Tues PM: Resolved byP0302R1
Proposed resolution:
Resolved by acceptance ofP0302R1.