[英]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.