While 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::functionis specified to pass the supplied arguments to the wrapped function withstd::forward. e.g. forstd::function<void(MoveTracker)>, the function call operator is equivalent toSince
std::forward<T>is equivalent tostd::movewhenTis 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 insidestd::function.This then also accounts for the problem you are encountering with using
std::bindas the wrapped function:std::bindis also specified to forward its parameters, and in this case it is being passed an rvalue reference resulting from thestd::forwardcall insidestd::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.