Во время работы над этим вопросом я заметил, что реализация 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
Мы видим здесь, что выполняется копия cm
(что представляется разумным, так как параметр byValue
принимается по значению), но затем есть два хода. Поскольку function
работает с копией cm
, тот факт, что он перемещает свой аргумент, можно рассматривать как несущественную деталь реализации. Однако это поведение вызывает некоторые проблемы при использовании function
вместе с 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!"
}
Разрешено ли это поведение стандартом? Разрешено ли std::function
перемещать свои аргументы? И если да, то нормально ли, что мы можем преобразовать оболочку, возвращаемую bind
в std::function
с параметрами по значению, даже если это вызывает неожиданное поведение при работе с несколькими вхождениями заполнителей?