31

我想将一个右值传递std::bind给一个在 C++0x 中采用右值引用的函数。我不知道该怎么做。例如:

#include <utility>
#include <functional>

template<class Type>
void foo(Type &&value)
{
    Type new_object = std::forward<Type>(value);    // move-construct if possible
}

class Movable
{
public:
    Movable(Movable &&) = default;
    Movable &operator=(Movable &&) = default;
};

int main()
{
    auto f = std::bind(foo<Movable>, Movable());
    f();    // error, but want the same effect as foo(Movable())
}
4

5 回答 5

33

失败的原因是,当您指定 时foo<Movable>,您要绑定的函数是:

void foo(Movable&&) // *must* be an rvalue
{
}

但是,传递的值std::bind不会是右值,而是左值(作为成员存储在结果bind函子的某处)。也就是说,生成的仿函数类似于:

struct your_bind
{
    your_bind(Movable arg0) :
    arg0(arg0)
    {}

    void operator()()
    {
        foo<int>(arg0); // lvalue!
    }

    Movable arg0;
};

构造为your_bind(Movable())。所以你可以看到这失败了,因为Movable&&无法绑定到Movable.†</p>

一个简单的解决方案可能是这样的:

auto f = std::bind(foo<Movable&>, Movable());

因为现在你调用的函数是:

void foo(Movable& /* conceptually, this was Movable& &&
                        and collapsed to Movable& */)
{
}

而且通话效果很好(当然,foo<const Movable&>如果需要,您可以这样做)。但一个有趣的问题是,我们是否可以让您原来的绑定工作,我们可以通过:

auto f = std::bind(foo<Movable>,
            std::bind(static_cast<Movable&&(&)(Movable&)>(std::move<Movable&>),
                Movable()));

也就是说,我们只是std::move调用之前的参数,所以它可以绑定。但是,哎呀,这很丑陋。强制转换是必需的,因为std::move它是一个重载函数,所以我们必须通过强制转换为所需的类型来指定我们想要的重载,消除其他选项

如果没有重载,它实际上不会那么糟糕std::move,就好像我们有类似的东西:

Movable&& my_special_move(Movable& x)
{
    return std::move(x);
}


auto f = std::bind(foo<Movable>, std::bind(my_special_move, Movable()));

这要简单得多。但是除非你有这样的函数,否则我认为很明显你可能只想指定一个更明确的模板参数。


† 这与在没有显式模板参数的情况下调用函数不同,因为显式指定它会消除推断它的可能性。( T&&, whereT是一个模板参数,可以推导出任何东西如果你允许的话。)

于 2011-02-02T06:53:42.340 回答
2

您可以使用 lambda 表达式。

auto f = [](){ foo(Movable()); };

这似乎是最简单的选择。

于 2011-02-02T11:31:14.097 回答
1

伙计们,我在这里http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html破解了一个完美的活页夹转发版本(限制为 1 个参数)

供参考,代码是

template <typename P>
class MovableBinder1
{
  typedef void (*F)(P&&);

private:
  F func_;
  P p0_;

public:
  MovableBinder1(F func, P&& p) :
    func_(func),
    p0_(std::forward<P>(p))
  {
    std::cout << "Moved" << p0_ << "\n";
  }

  MovableBinder1(F func, P& p) :
    func_(func),
    p0_(p)
  {
    std::cout << "Copied" << p0_ << "\n";
  }

  ~MovableBinder1()
  {
    std::cout << "~MovableBinder1\n";
  }

  void operator()()
  {
    (*func_)(std::forward<P>(p0_));
  }
};

从上面的概念证明中可以看出,它很有可能......

我看不出为什么 std::bind 与 std::move 不兼容... std::forward 毕竟是为了完美转发我不明白为什么没有 std::forwarding_bind ???

于 2012-11-11T13:08:13.960 回答
0

(这实际上是对 GMan 答案的评论,但我需要对代码进行一些格式化)。如果生成的仿函数实际上是这样的:

结构 your_bind
{
    your_bind(可移动 arg0):
    arg0(arg0)
    {}

    无效运算符()()
    {
        富(arg0);
    }

    可移动的 arg0;
};

然后

主函数()
{
    自动 f = your_bind(Movable());
    F(); // 没有错误!
}

编译没有错误。因为可以使用右值分配和初始化数据,然后将数据值传递给 foo() 的右值参数。
但是,我认为绑定实现直接从 foo() 签名中提取函数参数类型。即生成的函子是:

结构 your_bind
{
    your_bind(可移动 && arg0) :
    arg0(arg0) // **** 错误:无法从 Movable 转换为 Movable &&
    {}

    无效运算符()()
    {
        富(arg0);
    }

    可动&&arg0;
};

实际上,这确实无法初始化右值数据成员。也许,绑定实现根本没有正确地从函数参数类型中提取“未引用”类型,而是“按原样”将此类型用于仿函数的数据成员声明,​​而不修剪 &&。

正确的函子应该是:

结构 your_bind
{
    your_bind(Movable&& arg0) :
    arg0(arg0)
    {}

    无效运算符()()
    {
        富(arg0);
    }

    可移动的 arg0; // 修剪 && !!!
};


于 2011-02-02T11:22:04.763 回答
0

GManNickG 的回答又多了一项改进,我有一个很好的解决方案:

auto f = std::bind(
    foo<Movable>,
    std::bind(std::move<Movable&>, Movable())
);

(适用于 gcc-4.9.2 和 msvc2013)

于 2015-07-15T17:16:33.597 回答