2013-03-12 12 views
13

Próbuję zrozumieć sposób działania konstruktorów move i zadań przydziału w C++ 11, ale mam problemy z delegowaniem do klas nadrzędnych.Przenoszenie konstruktorów i dziedziczenie

Kod:

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; 
} 

Jednak, kiedy mogę skompilować i uruchomić pod VS2012, wyjście wskazuje, że lwartości wersje członków T0 są nazywane:

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

podobnej sytuacji (z trochę inny przypadek testowy) dzieje się z przypisaniami ruchu - operator przypisania ruchu T nazywa "normalnego" operatora przypisania T0.

Co robię źle?

Odpowiedz

16

jeden z bardziej Mylące rzeczy dotyczące funkcji przyjmujących wartości rvalue jako parametry są takie, że wewnętrznie traktują swoje parametry jako l-wartości. Ma to na celu uniemożliwienie przesuwania parametru przed jego użyciem, ale wymaga to przyzwyczajenia się. Aby rzeczywiście przenieść parametr, musisz wywołać std :: move (lub std :: forward) na nim. Więc trzeba zdefiniować konstruktor ruch jako:

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

i twój ruch operatora przypisania jak:

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

Ty zawsze tylko nazywając rzeczy klasie bazowej z lwartościami:

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 

Oznacza to, że trzeba:

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

oraz:

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

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

    return *this; 
}