Can std::function be move-constructed from rvalue reference to a temporary functor object?

你。 提交于 2019-12-06 17:06:04

问题


I have an untemplated functor object that I'm trying to store as a std::function inside another object. This object is really heavyweight, so it's marked as uncopyable, but it does have a move constructor. However, trying to construct a std::function, or assign it, from a temporary constructor fails.

Here is a minimal example to provoke the error.

// pretend this is a really heavyweight functor that can't be copied.
struct ExampleTest
{
    int x;
    int operator()(void) const {return x*2;}
    ExampleTest(  ) :x(0){}
    ExampleTest( int a ) :x(a){}

    // allow move
    ExampleTest( ExampleTest &&other ) :x(other.x) {};

private: // disallow copy, assignment
    ExampleTest( const ExampleTest &other );
    void operator=( const ExampleTest &other );
};

// this sometimes stores really big functors and other times stores tiny lambdas.
struct ExampleContainer
{
    ExampleContainer( int );
    std::function<int(void)> funct;
};

/******** ERROR:
 Compiler error: 'ExampleTest::ExampleTest' : cannot access private member 
 declared in class 'ExampleTest'
******************/
ExampleContainer::ExampleContainer( int x )
    : funct( ExampleTest( x ) ) 
{}

/******** ERROR:
 Compiler error: 'ExampleTest::ExampleTest' : cannot access private member 
 declared in class 'ExampleTest'
******************/
int SetExample( ExampleContainer *container )
{
    container->funct = ExampleTest();
    return container->funct();
}

In an even simpler construction, where I'm just making a local function, I also get the error:

int ContrivedExample(  )
{
    // extra parens to sidestep most vexing parse 
    std::function<int()> zug( (ExampleTest()) );
    /*** ERROR: 'ExampleTest::ExampleTest' : cannot access private member
         declared in class 'ExampleTest' */
    int troz = zug(  ) ;
    return troz;
}

So far as I can tell, in all of these cases, a temporary ExampleTest ought to be passed to the function constructor as an rvalue. Yet the compiler wants to copy them.

What gives? Is it possible to pass uncopyable (but move-copyable) functor objects to a std::function constructor? There are workarounds with pointers and so on, but I want to understand what is going on here.

The specific errors above are from Visual Studio 2012 with the CTP C++11 patch. GCC 4.8 and Clang 3 also fall down, with their own error messages.


回答1:


This object is really heavyweight, so it's marked as uncopyable, but it does have a move constructor.

If a functor is non-copyable, it does not meet the necessary requirements for being used with std::function. Paragraph 20.8.11.2.1/7 of the C++11 Standard specifies:

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

7 Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.




回答2:


std::function can be move-constructed from rvalue of a functor object. And most implementations do that.

The "my target must be copy-constructable" requirement of std::function is due to its own requirement of being copy-constructable. std::function's type is defined only by its target's signature(eg: void(int)) and std::function itself is defined by the standard to be copy-constructable. So when you copy-construct a std::function, it needs to call the copy-ctor of the its target(the underlying functor). So it requires its target having one. It has no other choices.

Having the requirement that the target being copy-constructable, the standard does not say that the implementations should copy, instead of move, when you construct a std::function from a rvalue callable object. The implemention will probably only call the move-ctor of your callable object.


More detailed additional information with examples and tests:

For example in gcc(MSVC is similar) implementation for the ctor of std::function from any callable object:

template<typename _Res, typename... _ArgTypes>
  template<typename _Functor, typename>
    function<_Res(_ArgTypes...)>::
    function(_Functor __f)
    : _Function_base()
    {
        typedef _Function_handler<_Signature_type, _Functor> _My_handler;

        // don't need to care about details below, but when it uses __f, it 
        // either uses std::move, or passes it by references
        if (_My_handler::_M_not_empty_function(__f))
        {
           _My_handler::_M_init_functor(_M_functor, std::move(__f));
           _M_invoker = &_My_handler::_M_invoke;
           _M_manager = &_My_handler::_M_manager;
        }
    }

passing by value of the argument of "_Functor __f" will use its move constructor if it has one, and it will use its copy constructor if it does not have a move ctor. As the following test program can demonstrate:

int main(){
    using namespace std;
    struct TFunctor
    {
        TFunctor() = default;
        TFunctor(const TFunctor&) { cout << "cp ctor called" << endl; }
        TFunctor(TFunctor&&) { cout << "mv ctor called" << endl; };
        void operator()(){}
    };

    {   //!!!!COPY CTOR of TFunctor is NEVER called in this scope
        TFunctor myFunctor;

        //TFunctor move ctor called here
        function<void()> myStdFuncTemp{ std::move(myFunctor) }; 

        function<void()> myStdFunc{ move(myStdFuncTemp) }; 
    }

    {   //TFunctor copy ctor is called twice in this scope
        TFunctor myFunctor;

        //TFunctor copy ctor called once here
        function<void()> myStdFuncTemp{ myFunctor };

        //TFunctor copy ctor called once here
        function<void()> myStdFunc{ myStdFuncTemp };
    }
}

Finally, you could make a unstd::function_only_movable which has almost everything the same with std::function but deletes its own copy ctor so it does not need to require the target callable object to have one copy ctor. You also need to only construct it from rvalue of callable objects.



来源:https://stackoverflow.com/questions/16639131/can-stdfunction-be-move-constructed-from-rvalue-reference-to-a-temporary-funct

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!