Luc*_*lle 17 c++ bind function c++11
在处理这个问题的过程中,我注意到GCC(v4.7)的实现std::function在它们被值出现时会移动它的论点.以下代码显示了此行为:
#include <functional>
#include <iostream>
struct CopyableMovable
{
CopyableMovable() { std::cout << "default" << '\n'; }
CopyableMovable(CopyableMovable const &) { std::cout << "copy" << '\n'; }
CopyableMovable(CopyableMovable &&) { std::cout << "move" << '\n'; }
};
void foo(CopyableMovable cm)
{ }
int main()
{
typedef std::function<void(CopyableMovable)> byValue;
byValue fooByValue = foo;
CopyableMovable cm;
fooByValue(cm);
}
// outputs: default copy move move
Run Code Online (Sandbox Code Playgroud)
我们在这里看到cm执行的副本(这似乎是合理的,因为byValue's参数是按值获取的),但是有两个动作.由于它function是在副本上运行cm,它移动其参数的事实可以被视为一个不重要的实现细节.但是,与以下一起使用时functionbind,此行为会导致一些问题:
#include <functional>
#include <iostream>
struct MoveTracker
{
bool hasBeenMovedFrom;
MoveTracker()
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker const &)
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker && other)
: hasBeenMovedFrom(false)
{
if (other.hasBeenMovedFrom)
{
std::cout << "already moved!" << '\n';
}
else
{
other.hasBeenMovedFrom = true;
}
}
};
void foo(MoveTracker, MoveTracker) {}
int main()
{
using namespace std::placeholders;
std::function<void(MoveTracker)> func = std::bind(foo, _1, _1);
MoveTracker obj;
func(obj); // prints "already moved!"
}
Run Code Online (Sandbox Code Playgroud)
标准是否允许这种行为?被std::function允许移动其论点?如果是这样,我们可以将返回的包装器bind转换为std::functionwith-value参数,这是正常的,即使在处理多个占位符时会触发意外行为吗?
Ant*_*ams 17
std::function指定将提供的参数传递给包装函数std::forward.例如std::function<void(MoveTracker)>,函数调用运算符相当于
void operator(CopyableMovable a)
{
f(std::forward<CopyableMovable>(a));
}
Run Code Online (Sandbox Code Playgroud)
由于std::forward<T>相当于std::movewhen T不是引用类型,因此在第一个示例中考虑了其中一个移动.第二种可能是必须通过内部的间接层std::function.
然后,这也说明了你在使用遇到的问题std::bind作为包装的函数:std::bind是还指定传送其参数,在此情况下,它被传递从产生的右值引用std::forward内部通话std::function.因此,绑定表达式的函数调用运算符将向每个参数转发rvalue引用.不幸的是,由于你重复使用占位符,它在两种情况下都是对同一个对象的右值引用,因此对于可移动类型,无论哪个构造首先移动值,第二个参数将得到一个空壳.