2564. [fund.ts.v2] std::experimental::function constructors taking allocator arguments may throw exceptions

Section: 4.2 [fund.ts.v2::func.wrap.func] Status: Resolved Submitter: Tim Song Opened: 2015-12-05 Last modified: 2022-11-22

Priority: 3

View all other issues in [fund.ts.v2::func.wrap.func].

View all issues with Resolved status.

Discussion:

Addresses: fund.ts.v2

[This is essentially LWG 2370, but deals with the fundamentals TS version rather than the one in the standard]

In 4.2 [fund.ts.v2::func.wrap.func] of library fundamentals TS, the constructors

template<class A> function(allocator_arg_t, const A&) noexcept;
template<class A> function(allocator_arg_t, const A&, nullptr_t) noexcept;

must type-erase and store the provided allocator, since the operator= specification requires using the "allocator specified in the construction of" the std::experimental::function object. This may require a dynamic allocation and so cannot be noexcept. Similarly, the following constructors

template<class A> function(allocator_arg_t, const A&, const function&); 
template<class A> function(allocator_arg_t, const A&, function&&);
template<class F, class A> function(allocator_arg_t, const A&, F);

cannot satisfy the C++14 requirement that they "shall not throw exceptions if [the function object to be stored] is a callable object passed via reference_wrapper or a function pointer" if they need to type-erase and store the allocator.

[2016-11-08, Issaquah]

Not adopted during NB comment resolution

[2022-10-12 LWG telecon]

Set status to "Open". This would be resolved by P0987R2.

[2022-11-22 Resolved by P0897R2 accepted in Kona. Status changed: Open → Resolved.]

Proposed resolution:

This wording is relative to N4562.

  1. Edit 4.2 [fund.ts.v2::func.wrap.func], class template function synopsis, as follows:

    namespace std {
      namespace experimental {
      inline namespace fundamentals_v2 {
    
        […]
    
        template<class R, class... ArgTypes>
        class function<R(ArgTypes...)> {
        public:    
          […]
          template<class A> function(allocator_arg_t, const A&) noexcept;
          template<class A> function(allocator_arg_t, const A&,
            nullptr_t) noexcept;
          […]
        };
    
        […]
    
      } // namespace fundamentals_v2
      } // namespace experimental
    
      […]
    
    } // namespace std
    
  2. Insert the following paragraphs after 4.2.1 [fund.ts.v2::func.wrap.func.con]/1:

    [Drafting note: This just reproduces the wording from C++14 with the "shall not throw exceptions for reference_wrapper/function pointer" provision deleted. — end drafting note]

    -1- When a function constructor that takes a first argument of type allocator_arg_t is invoked, the second argument is treated as a type-erased allocator (8.3). If the constructor moves or makes a copy of a function object (C++14 §20.9), including an instance of the experimental::function class template, then that move or copy is performed by using-allocator construction with allocator get_memory_resource().

    template <class A> function(allocator_arg_t, const A& a);
    template <class A> function(allocator_arg_t, const A& a, nullptr_t);
    

    -?- Postconditions: !*this.

    template <class A> function(allocator_arg_t, const A& a, const function& f);
    

    -?- Postconditions: !*this if !f; otherwise, *this targets a copy of f.target().

    -?- Throws: May throw bad_alloc or any exception thrown by the copy constructor of the stored callable object. [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]

    template <class A> function(allocator_arg_t, const A& a, function&& f);
    

    -?- Effects: If !f, *this has no target; otherwise, move-constructs the target of f into the target of *this, leaving f in a valid state with an unspecified value.

    template <class F, class A> function(allocator_arg_t, const A& a, F f);
    

    -?- Requires: F shall be CopyConstructible.

    -?- Remarks: This constructor shall not participate in overload resolution unless f is Callable (C++14 §20.9.11.2) for argument types ArgTypes... and return type R.

    -?- Postconditions: !*this if any of the following hold:

    • f is a null function pointer value.

    • f is a null member pointer value.

    • F is an instance of the function class template, and !f.

    -?- Otherwise, *this targets a copy of f initialized with std::move(f). [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]

    -?- Throws: May throw bad_alloc or any exception thrown by F's copy or move constructor.

    -2- 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.

    […]