Mentre lavoravo su this question, ho notato che l'implementazione di GCC (v4.7) di std::function
sposta i suoi argomenti quando sono presi per valore. Il codice seguente illustra questo comportamento:È `std :: function` permesso di spostare i suoi argomenti?
#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
Vediamo qui che viene eseguita una copia di cm
(che sembra ragionevole dal parametro s' il byValue
è presa dal valore), ma poi ci sono due mosse. Poiché function
funziona su una copia di cm
, il fatto che si sposti il suo argomento può essere visto come un dettaglio di implementazione non importante. Tuttavia, questo comportamento causa qualche guaio 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!"
}
È questo comportamento consentito dalla norma? std::function
è consentito spostare i suoi argomenti? E se è così, è normale che possiamo convertire il wrapper restituito da bind
in un std::function
con i parametri di valore, anche se questo innesca un comportamento imprevisto quando si ha a che fare con più occorrenze di segnaposto?
Mi sembra che il problema riguardi più i segnaposti rispetto a 'std :: function'. Vale a dire, il fatto che durante la creazione di un 'tie', lo spostamento viene utilizzato dall'argomento originale ad entrambi gli output previsti. –
È interessante notare che il compilatore Visual C++ 11 stampa "copia di default" nel primo esempio e non stampa "già spostato!" nel secondo. Mi chiedo se questa ulteriore mossa possa provenire da meccanismi interni di std :: function e/o di forwarding perfetto. –
@MatthieuM. Potresti elaborare? Non ho molta familiarità con l'implementazione dei segnaposto. Se il problema deriva dai segnaposto, come mai il problema non si pone quando si utilizza 'auto' per dedurre il tipo" bind-wrapper ", invece di usare' std :: function'? –