2012-06-26 58 views
1

我寫了一個簡單的鏈表,因爲最近的一次面試編程挑戰讓我看到了C++的生鏽程度。在我的列表中,我聲明瞭一個私人拷貝構造函數,因爲我想明確地避免製作任何拷貝(當然還有懶惰)。當我想通過擁有我的一個列表的值返回一個對象時,我遇到了一些麻煩。返回值優化和私人拷貝構造函數

class Foo 
{ 
    MyList<int> list; // MyList has private copy constructor 

    public: 
     Foo() {}; 
}; 

class Bar 
{ 
    public: 
     Bar() {}; 

     Foo getFoo() 
     { 
     return Foo(); 
     } 
}; 

我得到一個編譯器錯誤,說我MyList有一個私人拷貝構造函數,當我嘗試按值返回一個Foo對象時。應該使用Return-Value-Optimization否定任何複製的需要?我是否需要編寫一個拷貝構造函數?我從來沒有聽說過移動構造函數,直到我開始尋找這個問題的解決方案,那是最好的解決方案嗎?如果是這樣,我將不得不閱讀它們。如果不是,那麼解決這個問題的首選方法是什麼?

+1

RVO是一種優化。如果你不允許複製構造,那麼你將無法複製構造。如果你允許的話,可以申請RVO。 – mfontanini

+0

優化不應改變行爲,相反,編譯器只有在可以保證預期行爲時才能執行優化。要通過複製返回,該對象必須是可複製的。 – lvella

+3

@lvella:儘管在這種情況下,優化*被允許改變行爲 - 即使副本有副作用也可以省略副本。但是您確認複製構造函數必須可訪問。 –

回答

3

基本問題是,按值返回可能是拷貝。該標準並不要求C++實現在適用時應用copy-elision。這就是爲什麼該對象仍然必須是可複製的:以便實現決定何時使用它不影響代碼是否格式良好。

無論如何,它不一定適用於用戶可能會喜歡的每個副本。例如,沒有複製分配的省略。

我覺得你的選擇是:

  • 實現適當的副本。如果有人因爲複製而導致緩慢的程序,那麼他們的分析人員會告訴他們,如果你不想讓它停下來,你就不需要做它的工作。
  • 實施適當的移動,但沒有複製(僅限C++ 11)。
  • 更改getFoo採取Foo&(或可能Foo*)參數,並通過某種方式避免副本突變其對象。有效的swap會派上用場。如果getFoo確實返回了一個默認構造的Foo(如您的示例),這是毫無意義的,因爲調用者在調用getFoo之前需要構造Foo
  • 返回動態分配Foo包裝在一個智能指針:auto_ptrunique_ptr。定義爲創建對象並將其唯一所有權轉讓給其調用者的功能不應該返回shared_ptr,因爲它沒有release()函數。
  • 提供了一個拷貝構造函數,但是如果它曾經被使用過,它會以某種方式炸燬(無法鏈接,放棄,拋出異常)。這樣做的問題是(1)它註定要失敗,但編譯器什麼也不說,(2)你正在執行實施的質量,所以如果有人故意禁止RVO出於任何原因,你的班級將無法工作。

我可能錯過了一些。

5

該標準明確指出構造函數仍然需要可訪問,即使它被優化了。最近的草稿請參見12.8/32

我更喜歡在這種情況下製作一個可移動和不可複製的對象。它使所有權非常清晰明確。

否則,您的用戶可以始終使用shared_ptr。隱藏共享所有權至多是一個值得懷疑的想法(除非你能保證你的所有價值都是不變的)。

+0

那麼我最好的舉動是什麼?爲了編譯器的緣故,允許淺拷貝並希望沒有人真正創建一個? – jlunavtgrad

+0

@jlunavtgrad我在這樣的設計決定中增加了2美分。 – pmr

2

該解決方案將實現您自己的複製構造函數,該構造函數將使用其他方法MyList來實現複製語義。

...我想明確地避免任何副本

你必須選擇。要麼你不能複製一個對象,如std::istream;那麼你必須在指針/引用中保存這些對象,因爲這些對象可以被複制(在C++ 11中,你可以使用移動語義代替)。或者你實現複製構造函數,這可能更容易解決每個需要複製副本的問題。