2013-05-16 35 views
14

我經常發現自己正在爲有許多成員變量的類編寫單調乏味的移動構造函數。他們看起來像下面這樣:委託給默認的移動構造函數

A(A && rhs) : 
    a(std::move(rhs.a)), 
    b(std::move(rhs.b)), 
    c(std::move(rhs.c)), 
    d(std::move(rhs.d)) { 
    some_extra_work(); 
} 

也就是說,他們執行所有與默認移動構造函數相關的動作,然後一些[執行額外平凡的任務。理想情況下,我將委託給默認的移動構造函數,然後執行額外的工作,但是定義我自己的移動構造函數的行爲阻止了默認實現的定義,這意味着沒有什麼可委派給。

有沒有一種很好的方法來解決這種反模式?

+6

很難對這種抽象的層面說,但也許你可以分開'A'分爲兩大類,把一個到另一個?一個會有所有的默認構造函數,另外一個會執行'some_extra_work'。檢查*規則零*。 – zch

+0

不知道這是否適用,但會像'A(A && rhs,int):A(rhs){}'不起作用? – Damon

+3

具有默認移動構造函數的中間基類可能會有所幫助。 –

回答

3

更新:忽略此答案的第一部分,並跳到最後有一個更好的解決方案。

裹在一個新的類型的額外工作,並從它繼承:

class A; 

struct EW 
{ 
    EW(EW&&); 
}; 

class A : private EW 
{ 
    friend class EW; 
public: 
    A(A&&) = default; 
}; 

EW::EW(EW&&) { A* self = static_cast<A*>(this); self->some_extra_work(); } 

你也可以用一個數據成員,而不是一個基類做,但你需要使用offsetof一些兩輪牛車(這對於非標準佈局類型是未定義的)或者使用偷偷指針算術的手卷類似物。使用繼承允許您使用static_cast進行轉換。

如果some_extra_work()有這將不會工作要做後的成員都被初始化因爲基類首先被初始化。

或者,如果額外的工作實際上是在您正在移動的右值對象上進行操作,那麼您應該將這些成員包裝到從中移動時自動執行工作的類型中。我tidy_ptr類型,我用它來實現Rule of Zero

class A 
{ 
    tidy_ptr<D> d; 
public: 
    A() = default; 
    A(const A&) = default; 
    A(A&& r) = default; // Postcondition: r.d == nullptr 
}; 
+0

我想知道,是不是在成員移動ctors之前調用的基類移動ctor? – Xeo

+0

這是肯定的,如果額外的工作依賴於其他成員,將無法工作。當我寫到我沒有閱讀OP的評論時說:「some_extra_work()的典型例子是'rhs.d = 0;'」 –

+0

...答案的第二部分在這種情況下起作用 –