【问题标题】:Can't invoke or assign a std::function that has an rvalue reference as an argument (Visual C++)无法调用或分配具有右值引用作为参数的 std::function (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 会议上宣布。

【问题讨论】:

    标签: visual-c++ rvalue-reference


    【解决方案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(“无法生成赋值运算符”)。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多