繁体   English   中英

通过std :: bind传递rvalues

[英]Passing rvalues through std::bind

我想通过std::bind将一个rvalue传递给一个在C ++ 0x中采用rvalue引用的函数。 我无法弄清楚该怎么做。 例如:

#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())
}

失败的原因是因为当你指定foo<Movable> ,你绑定的函数是:

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

但是, std::bind传递的值不是rvalue,而是lvalue(在结果bind仿函数中的某个位置存储)。 那,生成的仿函数类似于:

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

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

    Movable arg0;
};

构造为your_bind(Movable()) 所以你可以看到这个失败,因为Movable&&无法绑定到Movable 。†

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

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是一个重载函数,所以我们要指定超载,我们希望通过强制转换为所需的类型,从而消除了其他选项。

如果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&& ,其中T是模板参数, 如果你允许 ,可以推导出任何东西 。)

伙计们,我已经破解了一个完美的转发版转发版(限于1个参数) http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html

供参考,代码是

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 ???

GManNickG的答案还有一个改进,我有很好的解决方案:

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

(适用于gcc-4.9.2和msvc2013)

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

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

    void operator()()
    {
        foo(arg0);
    }

    Movable arg0;
};

然后

int main()
{
    auto f = your_bind(Movable());
    f();    // No errors!
}

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

struct your_bind
{
    your_bind(Movable && arg0) :
    arg0(arg0) // ****  Error:cannot convert from Movable to Movable &&
    {}

    void operator()()
    {
        foo(arg0); 
    }

    Movable&& arg0;
};

实际上,这确实无法初始化rvalue数据成员。 也许,绑定implpementation只是没有从函数参数类型中正确地提取“未引用”类型,并且使用此类型进行仿函数的数据成员声明“按原样”,而不修剪&&。

正确的仿函数应该是:

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

    void operator()()
    {
        foo(arg0); 
    }

    Movable arg0; // trim && !!!
};

你可以使用lambda表达式。

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

这似乎是最简单的选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM