簡體   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