简体   繁体   English

通过std :: bind传递rvalues

[英]Passing rvalues through std::bind

I want to pass an rvalue through std::bind to a function that takes an rvalue reference in C++0x. 我想通过std::bind将一个rvalue传递给一个在C ++ 0x中采用rvalue引用的函数。 I can't figure out how to do it. 我无法弄清楚该怎么做。 For example: 例如:

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

The reason this fails is because when you specify foo<Movable> , the function you're binding to is: 失败的原因是因为当你指定foo<Movable> ,你绑定的函数是:

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

However, the value passed by std::bind will not be an rvalue, but an lvalue (stored as a member somewhere in the resulting bind functor). 但是, std::bind传递的值不是rvalue,而是lvalue(在结果bind仿函数中的某个位置存储)。 That, is the generated functor is akin to: 那,生成的仿函数类似于:

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

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

    Movable arg0;
};

Constructed as your_bind(Movable()) . 构造为your_bind(Movable()) So you can see this fails because Movable&& cannot bind to Movable .† 所以你可以看到这个失败,因为Movable&&无法绑定到Movable 。†

A simple solution might be this instead: 一个简单的解决方案可能是这样:

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

Because now the function you're calling is: 因为现在你正在调用的函数是:

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

And the call works fine (and, of course, you could make that foo<const Movable&> if desired). 调用工作正常(当然,如果需要,你可以使foo<const Movable&> )。 But an interesting question is if we can get your original bind to work, and we can via: 但一个有趣的问题是,如果我们可以让你的原始绑定工作,我们可以通过:

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

That is, we just std::move the argument before we make the call, so it can bind. 也就是说,我们只是std::move在我们进行调用之前std::move参数,因此它可以绑定。 But yikes, that's ugly. 但是,哎呀,这很难看。 The cast is required because std::move is an overloaded function, so we have to specify which overload we want by casting to the desired type, eliminating the other options. 演员是必需的,因为std::move是一个重载函数,所以我们要指定超载,我们希望通过强制转换为所需的类型,从而消除了其他选项。

It actually wouldn't be so bad if std::move wasn't overloaded, as if we had something like: 如果std::move没有重载,它实际上不会那么糟糕,好像我们有类似的东西:

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


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

Which is much simpler. 哪个更简单。 But unless you have such a function laying around, I think it's clear you probably just want to specify a more explicit template argument. 但除非你有这样的功能,否则我认为很明显你可能只想指定一个更明确的模板参数。


† This is different than calling the function without an explicit template argument, because explicitly specifying it removes the possibility for it to be deduced. †这与在没有显式模板参数的情况下调用函数不同,因为明确指定它会消除推导它的可能性。 ( T&& , where T is a template parameter, can be deduced to anything , if you let it be .) T&& ,其中T是模板参数, 如果你允许 ,可以推导出任何东西 。)

Guys i have hacked up a perfect forwarding version of a binder(limited to 1 param) here http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html 伙计们,我已经破解了一个完美的转发版转发版(限于1个参数) http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html

For reference the code is 供参考,代码是

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_));
  }
};

As u can see from the above proof of concept, its very possible... 正如你从上面的概念证明中可以看到的,它很可能......

I see no reason why std::bind is incompatible with std::move... std::forward is after all for perfect forwarding I dont understand why there isnt a std::forwarding_bind ??? 我没有看到为什么std :: bind与std :: move不兼容... std :: forward毕竟是完美的转发我不明白为什么没有std :: forwarding_bind ???

One more improvement in GManNickG's answer and I've got pretty solution: GManNickG的答案还有一个改进,我有很好的解决方案:

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

(works in gcc-4.9.2 and msvc2013) (适用于gcc-4.9.2和msvc2013)

(This is actually a comment to GMan's answer, but I need some formatting for the code). (这实际上是对GMan答案的评论,但我需要对代码进行一些格式化)。 If generated functor actually is like this: 如果生成的functor实际上是这样的:

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

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

    Movable arg0;
};

then 然后

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

compliles without errors. compliles没有错误。 as it's possible to assign and initialize data with rvalue and then pass a data value to rvalue argument of the foo(). 因为可以使用rvalue分配和初始化数据,然后将数据值传递给foo()的rvalue参数。
However, I suppose that bind implementation extracts function argument type directly from foo() signature. 但是,我想绑定实现直接从foo()签名中提取函数参数类型。 ie the generated functor is: 即生成的仿函数是:

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

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

    Movable&& arg0;
};

and indeed, this really fails to initialize rvalue data member. 实际上,这确实无法初始化rvalue数据成员。 Perhaps,the bind implpementation simply does not correctly extract "unreferenced" type from function argument type and uses this type for functor's data member declaration "as is", without trimming &&. 也许,绑定implpementation只是没有从函数参数类型中正确地提取“未引用”类型,并且使用此类型进行仿函数的数据成员声明“按原样”,而不修剪&&。

the correct functor should be: 正确的仿函数应该是:

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

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

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

You could use a lambda expression. 你可以使用lambda表达式。

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

This would seem to be the simplest option. 这似乎是最简单的选择。

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

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