Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 542
Crashworks Avatar asked May 19 '13 20:05

Crashworks


People also ask

What does std :: move do?

std::move. std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

What is rvalue reference in c++ 11?

In C++11, however, the rvalue reference lets us bind a mutable reference to an rvalue, but not an lvalue. In other words, rvalue references are perfect for detecting whether a value is a temporary object or not.

Why rvalue reference?

Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.


1 Answers

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.

like image 118
Andy Prowl Avatar answered Sep 21 '22 03:09

Andy Prowl