无法调用或分配具有右值引用作为参数的 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(&amp;f); 不起作用;因此,我引入了一个执行完美转发的中间步骤,以便将呼叫解析为:f( move( (Object&amp;)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&lt;void (Object)&gt; 是可行的,因为呼叫站点数量很少,所以我会这样做。 (你说得对,我没有具体说明我在寻找什么样的解决方法。) 更新:我的解决方法也不起作用。当我调用函数时,复制构造函数被调用,即使我移动了参数。我认为这是上面链接的 MS 错误 649274 的另一方面。下一个最佳选择是function&lt;void (Object&amp;)&gt;,它确实有效。 又一个更新:我改变主意并实现了@DyP 的包装类,它允许我在很多地方使用Object&amp;&amp; 而不是Object&amp;更有意义。它工作得很好。对于wrapper 的定义,我确实必须禁用警告 4512(“无法生成赋值运算符”)。非常感谢!

以上是关于无法调用或分配具有右值引用作为参数的 std::function (Visual C++)的主要内容,如果未能解决你的问题,请参考以下文章

右值引用成员变量有啥用

将 C 函数和参数作为右值引用传递

右值引用,移动语义,完美转发

具有适用于左值和右值的引用参数的 C++ 函数

函数不将右值引用作为参数? [复制]

将此指针分配给对指针的右值引用