2013-03-12 16 views
13

Sto cercando di capire il modo in cui i costruttori di movimento e le operazioni di assegnazione funzionano in C++ 11 ma ho problemi con la delega alle classi padre.Spostare i costruttori e l'ereditarietà

Il codice:

class T0 
{ 
public: 
    T0() { puts("ctor 0"); } 
    ~T0() { puts("dtor 0"); } 
    T0(T0 const&) { puts("copy 0"); } 
    T0(T0&&) { puts("move 0"); } 
    T0& operator=(T0 const&) { puts("assign 0"); return *this; } 
    T0& operator=(T0&&) { puts("move assign 0"); return *this; } 
}; 

class T : public T0 
{ 
public: 
    T(): T0() { puts("ctor"); } 
    ~T() { puts("dtor"); } 
    T(T const& o): T0(o) { puts("copy"); } 
    T(T&& o): T0(o) { puts("move"); } 
    T& operator=(T const& o) { puts("assign"); return static_cast<T&>(T0::operator=(o)); } 
    T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(o)); } 
}; 

int main() 
{ 
    T t = std::move(T()); 
    return 0; 
} 

Tuttavia, quando compilo e corro sotto VS2012, l'output indica che le versioni lvalue dei membri T0 sono chiamati:

ctor 0 
ctor 
copy 0 <-- 
move <-- 
dtor 
dtor 0 
dtor 
dtor 0 

Una situazione analoga (con un caso di test leggermente diverso) avviene con le assegnazioni di movimento - l'operatore di assegnazione del movimento di T chiama l'operatore di assegnazione "normale" di T0.

Cosa sto sbagliando?

risposta

16

Uno dei più confondere le cose sulle funzioni che prendono i riferimenti di valore come parametri è che internamente trattano i loro parametri come lvalue. Questo per evitare di spostare il parametro prima del previsto, ma ci vuole un po 'per abituarsi. Per spostare effettivamente il parametro, devi chiamare std :: move (o std :: forward) su di esso. Quindi è necessario definire il costruttore mossa come:

T(T&& o): T0(std::move(o)) { puts("move"); } 

e la vostra mossa come operatore di assegnazione:

T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(std::move(o))); } 
5

Stai sempre e solo chiamando la roba di classe di base con lvalue:

void foo(int&){} // A 
void foo(int&&){} // B 

void example(int&& x) 
{ 
    // while the caller had to use an rvalue expression to pass a value for x, 
    // since x now has a name in here it's an lvalue: 
    foo(x); // calls variant A 
} 

example(std::move(myinteger)); // rvalue for us, lvalue for example 

Cioè, è necessario:

T(T&& o): 
T0(std::move(o)) // rvalue derived converts to rvalue base 
{ 
    puts("move"); 
} 

E:

T& operator=(T&& o) 
{ 
    puts("move assign"); 

    T0::operator=(std::move(o))); 

    return *this; 
} 
Problemi correlati