Can't invoke or assign a std::function that has an rvalue reference as an argument (Visual C++)

女生的网名这么多〃 提交于 2019-12-11 19:19:25

问题


Seems like Visual C++'s std::function<> doesn't handle functions with rvalue refs as arguments. Can anyone suggest a workaround?

#include <functional>
using namespace std;

class Object { };

void f(Object&&) { }
auto g = [](Object&&){ };
function<void(Object&&)> h;

int main()
{
   Object o;
   f(move(o));
   g(move(o));

   // Uncomment any one of the following lines, and we get an error from the instantiation
   // of std::function: "error C2664: You cannot bind an lvalue to an rvalue reference"

   //h(move(o));
   //h = g;
   //h = f;

   return 0;
}

This is Visual Studio 2010. I am not using /Za (so it is not this problem).

Update after some research: The code compiles in Clang, so I am pretty sure it is a Microsoft bug. It might be this one, fixed in VC11: 649274

Correction to the update: The MS bug is not fixed in VC11. From the link:

our first opportunity will be the "out of band" release between VC11 and VC12 that Herb Sutter announced at the GoingNative 2012 conference.


回答1:


I'm not sure what workaround you'd like here. Assuming you cannot change the call expression of the function object and the target signature, you can wrap the rvalue reference and pass the wrapped object (a temporary) via const ref. Essentially, the call expands to: f( wrap(move(o)) );

I suspect there's a problem with perfect forwarding, because binding i = bind(&f); does not work; therefore I've introduced an intermediate step performing perfect forwarding, such that the call is resolved to: f( move( (Object&)wrap( move(o) ) ) );

#include <iostream>
#include <functional>
using namespace std;

struct Object { int m; };

// target function with fixed signature (assuming we cannot change that)
void f(Object&& p) { p.m = 42; std::cout << p.m; };

// was surprised I didn't find any method to chain functions in the StdLib
// so here's my own:
template < typename F1, typename F2, typename P1 >
auto chain2(F1 f1, F2 f2, P1&& p1)
    -> decltype( f1(f2( std::forward<P1>(p1) )) )
{
    return f1( f2( std::forward<P1>(p1) ) );
}
// a special bind version; mostly syntactic sugar
// note you can also deduce the first template parameter; would be more work
// and not necessary here
template < typename P1, typename F1, typename F2 >
auto bind_chain(F1 f1, F2 f2)
  -> decltype( std::bind( &chain2<F1,F2,P1>, f1, f2, std::placeholders::_1 ) )
{
    return std::bind( &chain2<F1,F2,P1>, f1, f2, std::placeholders::_1 );
}

// as `std::move` is overloaded, we make things a little bit simpler;
// we later will need to get a function pointer on this, that's why
// I'd like to avoid too much overloading
template < typename T >
// for a certain reason, cannot use && here --------v, clang++3.2 accepts it
typename std::remove_reference<T>::type && my_move(T& p)
{
    return std::move(p);
}

struct wrapper
{
    Object&& m;
    wrapper(Object&& p) : m(std::move(p)) {}
    operator Object&() const { return m; }
    // alternatively:
    // operator Object&&() const { return std::move(m); }
};

int main()
{
   Object o;

   // we'll need to call the functor with an const ref
   function<void(wrapper const&)> i;

   // chaining the conversion to rvalue ref with the target function
   i = bind_chain<wrapper const&>( &f, &my_move<Object> );

   i( move(o) );

   return 0;
}


来源:https://stackoverflow.com/questions/16155395/cant-invoke-or-assign-a-stdfunction-that-has-an-rvalue-reference-as-an-argume

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