Mientras trabajaba en this question, noté que la implementación de std::function
de GCC (v4.7) mueve sus argumentos cuando se toman por valor. El código siguiente muestra este comportamiento:¿Está permitido `` std :: function` mover sus argumentos?
#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
Vemos aquí que se realiza una copia de cm
(lo que parece razonable, ya que el parámetro byValue
's se toma por valor), pero entonces hay dos movimientos. Como function
está funcionando en una copia de cm
, el hecho de que mueva su argumento puede verse como un detalle de implementación sin importancia. Sin embargo, este comportamiento hace que algunos problemas 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!"
}
¿Este comportamiento es permitido por la norma? ¿Está permitido std::function
mover sus argumentos? Y si es así, ¿es normal que podamos convertir el contenedor devuelto por bind
en un std::function
con parámetros de valor por valor agregado, aunque esto desencadena un comportamiento inesperado cuando se trata de varias ocurrencias de marcadores de posición?
Me parece que el problema es más con marcadores de posición que 'std :: function'. A saber, el hecho de que al crear un "empate", el movimiento se utiliza desde el argumento original a ambos resultados esperados. –
Curiosamente, el compilador de Visual C++ 11 imprime "movimiento de copia predeterminado" en el primer ejemplo y no imprime "ya movido". en el segundo. Me pregunto si este movimiento adicional puede provenir del funcionamiento interno de std :: function y/o el reenvío perfecto. –
@MatthieuM. ¿Podrías elaborar? No estoy muy familiarizado con la implementación de marcadores de posición. Si el problema proviene de los marcadores de posición, ¿cómo es posible que no surja el problema al usar 'auto' para deducir el tipo" bind-wrapper ", en lugar de usar' std :: function'? –