2013-03-12 129 views
13

我想了解移動構造函數和賦值操作在C++ 11中的工作方式,但我在委託給父類時遇到了問題。移動構造函數和繼承

代碼:

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

然而,當我編譯和VS2012下運行,在輸出指示T0成員的左值版本被稱爲:

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

類似的情況(與一個稍微不同的測試用例)發生在移動賦值 - T的移動賦值運算符稱爲T0的「正常」賦值運算符。

我在做什麼錯?

回答

16

一個更混淆有關將右值引用作爲參數的函數的事情是在內部將它們的參數視爲左值。這是爲了防止您在開始之前移動參數,但需要一些時間來適應。爲了實際移動參數,必須在其上調用std :: move(或std :: forward)。所以,你需要確定你的移動構造函數爲:

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

和您的移動賦值運算符爲:

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

你永遠只能調用基類的東西,用左值:

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 

也就是說,你需要:

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

和:

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

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

    return *this; 
}