是否允许`std :: function`移动它的参数? [英] Is `std::function` allowed to move its arguments?
问题描述
在研究这个问题时,我注意到GCC(v4.7)的实现 std :: function
在按值创建时移动它的参数。以下代码显示了此行为:
#include< functional>
#include< iostream>
struct CopyableMovable
{
CopyableMovable(){std :: cout<< 默认<< \\\
; }
CopyableMovable(CopyableMovable const&){std :: cout<< 复制<< \\\
; }
CopyableMovable(CopyableMovable&& amp;){std :: cout<< 移动<< \\\
; }
};
void foo(CopyableMovable cm)
{}
int main()
{
typedef std :: function< void(CopyableMovable )>根据值;
byValue fooByValue = foo;
CopyableMovable cm;
fooByValue(cm);
}
//输出:默认复制移动移动
我们在这里看到执行 cm
的副本(这似乎是合理的,因为 byValue
的参数是按值取值的),但随后有两个动作。由于函数
在 cm
的副本上运行,因此它移动其参数的事实可被视为不重要的实现详情。但是,当将函数
与) > bind
:
#include< functional>
#include< iostream>
struct MoveTracker
{
bool hasBeenMovedFrom;
MoveTracker()
:hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker常量&)
:hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker&& other)
:hasBeenMovedFrom(false)
{
if(other.hasBeenMovedFrom)
{
std: :cout<< 已经移动! << \\\
;
}
else
{
other.hasBeenMovedFrom = true;
}
}
};
foo(MoveTracker,MoveTracker){}
$ b $ int main()
{
using namespace std :: placeholders;
std :: function< void(MoveTracker)> func = std :: bind(foo,_1,_1);
MoveTracker obj;
func(obj); //打印已经移动!
}
标准允许这种行为吗?是否允许 std :: function
移动其参数?如果是这样,我们可以通过将 bind
返回的包装转换为
std :: function
被指定用 std :: forward
传递提供的参数给包装函数。例如对于 std :: function< void(MoveTracker)>
,函数调用操作符相当于
void operator(CopyableMovable a)
{
f(std :: forward< CopyableMovable>(a));由于 std :: forward< T>
>当$ T
不是引用类型时,code>相当于 std :: move
,这占一个你的第一个例子中的举动。有可能第二个来自 std :: function
中的间接层。
然后也解释了使用 std :: bind
作为包装函数遇到的问题: std :: bind
是也指定转发它的参数,在这种情况下,它将在<$ c中调用由 std :: forward
调用产生的右值引用$ C>的std ::功能
。因此,bind表达式的函数调用运算符会将右值引用转发给每个参数。不幸的是,既然你已经重用了占位符,那么在这两种情况下,它都是对同一对象的右值引用,所以对于可移动类型,首先构造的将移动该值,而第二个参数将获得空壳。 b $ bWhile working on this question, I noticed that GCC (v4.7)'s implementation of std::function
moves its arguments when they are taken by value. The following code shows this behavior:
#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
We see here that a copy of cm
is performed (which seems reasonable since the byValue
's parameter is taken by value), but then there are two moves. Since function
is operating on a copy of cm
, the fact that it moves its argument can be seen as an unimportant implementation detail. However, this behavior causes some trouble when using function
together with bind
:
#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!"
}
Is this behavior allowed by the standard? Is std::function
allowed to move its arguments? And if so, is it normal that we can convert the wrapper returned by bind
into a std::function
with by-value parameters, even though this triggers unexpected behavior when dealing with multiple occurrences of placeholders?
解决方案 std::function
is specified to pass the supplied arguments to the wrapped function with std::forward
. e.g. for std::function<void(MoveTracker)>
, the function call operator is equivalent to
void operator(CopyableMovable a)
{
f(std::forward<CopyableMovable>(a));
}
Since std::forward<T>
is equivalent to std::move
when T
is not a reference type, this accounts for one of the moves in your first example. It's possible that the second comes from having to go through the indirection layers inside std::function
.
This then also accounts for the problem you are encountering with using std::bind
as the wrapped function: std::bind
is also specified to forward its parameters, and in this case it is being passed an rvalue reference resulting from the std::forward
call inside std::function
. The function call operator of your bind expression is thus forwarding an rvalue reference to each of the arguments. Unfortunately, since you've reused the placeholder, it's an rvalue reference to the same object in both cases, so for movable types whichever is constructed first will move the value, and the second parameter will get an empty shell.
这篇关于是否允许`std :: function`移动它的参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!