Title
Contradictory requirements for std::function and std::reference_wrapper
Status
c++17
Section
[func.wrap.func.con]
Submitter
Jonathan Wakely

Created on 2016-10-13.00:00:00 last changed 89 months ago

Messages

Date: 2016-11-21.05:09:01

Proposed resolution:

This wording is relative to N4606.

  1. Modify [func.wrap.func.con] p4 and p6 the same way, as shown:

    function(const function& f);
    

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

    -4- Throws: shall not throw exceptions if f's target is a callable object passed viaspecialization of reference_wrapper or a function pointer. Otherwise, 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]

    function(function&& f);
    

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

    -6- Throws: shall not throw exceptions if f's target is a callable object passed viaspecialization of reference_wrapper or a function pointer. Otherwise, may throw bad_alloc or any exception thrown by the copy or move 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]

Date: 2016-11-15.00:00:00

[ 2016-11-12, Issaquah ]

Sat AM: Priority 0; move to Ready

Date: 2016-10-13.00:00:00

template<class F> function(F f) says that the effects are "*this targets a copy of f" which seems pretty clear that if F is reference_wrapper<CallableType> then the target is a reference_wrapper<CallableType>.

But the function copy and move constructors say "shall not throw exceptions if f's target is a callable object passed via reference_wrapper or a function pointer." From the requirement above it's impossible for the target to be "a callable object passed via reference_wrapper" because if the function was constructed with such a type then the target is the reference_wrapper not the callable object it wraps.

This matters because it affects the result of function::target_type(), and we have implementation divergence. VC++ and libc++ store the reference_wrapper as the target, but libstdc++ and Boost.Function (both written by Doug Gregor) unwrap it, so the following fails:

#include <functional>
#include <cassert>

int main()
{
 auto f = []{};
 std::function<void()> fn(std::ref(f));
 assert(fn.target<std::reference_wrapper<decltype(f)>>() != nullptr);
}

If std::function is intended to deviate from boost::function this way then the Throws element for the copy and move constructors is misleading, and should be clarified.

History
Date User Action Args
2017-07-30 20:15:43adminsetstatus: wp -> c++17
2017-03-05 23:41:16adminsetstatus: ready -> wp
2016-11-21 05:09:01adminsetmessages: + msg8664
2016-11-21 05:09:01adminsetstatus: new -> ready
2016-10-15 11:56:05adminsetmessages: + msg8561
2016-10-13 00:00:00admincreate