无法调用或分配具有右值引用作为参数的 std::function (Visual C++)
Posted
技术标签:
【中文标题】无法调用或分配具有右值引用作为参数的 std::function (Visual C++)【英文标题】:Can't invoke or assign a std::function that has an rvalue reference as an argument (Visual C++) 【发布时间】:2013-04-22 19:42:19 【问题描述】:似乎 Visual C++ 的 std::function<>
不处理以右值引用作为参数的函数。任何人都可以提出解决方法吗?
#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;
这是 Visual Studio 2010。我没有使用 /Za(所以它不是 this problem)。
研究后更新:代码在 Clang 中编译,所以我很确定这是 Microsoft 的错误。可能是这个,在VC11中修复:649274
更正更新: VC11 中未修复 MS 错误。从链接:
我们的第一个机会是 Herb Sutter 在 VC11 和 VC12 之间的“带外”版本 在 GoingNative 2012 会议上宣布。
【问题讨论】:
【参考方案1】:我不确定您在这里想要什么解决方法。假设您无法更改函数对象的调用表达式和目标签名,则可以包装右值引用并通过 const ref 传递被包装的对象(临时)。
本质上,调用扩展为:f( wrap(move(o)) );
我怀疑完美转发存在问题,因为绑定i = bind(&f);
不起作用;因此,我引入了一个执行完美转发的中间步骤,以便将呼叫解析为: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;
【讨论】:
这很有趣。我认为不可能保留调用语法!但是在我的情况下,将签名更改为function<void (Object)>
是可行的,因为呼叫站点数量很少,所以我会这样做。 (你说得对,我没有具体说明我在寻找什么样的解决方法。)
更新:我的解决方法也不起作用。当我调用函数时,复制构造函数被调用,即使我移动了参数。我认为这是上面链接的 MS 错误 649274 的另一方面。下一个最佳选择是function<void (Object&)>
,它确实有效。
又一个更新:我改变主意并实现了@DyP 的包装类,它允许我在很多地方使用Object&&
而不是Object&
更有意义。它工作得很好。对于wrapper
的定义,我确实必须禁用警告 4512(“无法生成赋值运算符”)。非常感谢!以上是关于无法调用或分配具有右值引用作为参数的 std::function (Visual C++)的主要内容,如果未能解决你的问题,请参考以下文章