我想通过std::bind
将rvalue传递给一个在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())
}
答案 0 :(得分:27)
失败的原因是因为当您指定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
。†
一个简单的解决方案可能是:
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&&
,其中T
是模板参数,可以推断为任何,if you let it be。)
答案 1 :(得分:1)
伙计们,我已经在这里破解了一个完美的转发版转发版(限于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 ???
答案 2 :(得分:0)
(这实际上是对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! }
汇编没有错误。因为可以使用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只是不能正确地从函数参数类型中提取“未引用”类型,并且将这种类型用于仿函数的数据成员声明“按原样”,而不是修剪&amp;&amp;。
正确的仿函数应该是:
struct your_bind { your_bind(Movable&& arg0) : arg0(arg0) {} void operator()() { foo(arg0); } Movable arg0; // trim && !!! };
答案 3 :(得分:0)
您可以使用lambda表达式。
auto f = [](){ foo(Movable()); };
这似乎是最简单的选择。
答案 4 :(得分:0)
GManNickG的答案还有一个改进,我有很好的解决方案:
auto f = std::bind(
foo<Movable>,
std::bind(std::move<Movable&>, Movable())
);
(适用于gcc-4.9.2和msvc2013)