3

如果一個C++(隱式或顯式的)值構造通過接受其參數(一個或多個)值或參考給const,當它需要存儲在其對象參數(一個或多個)任何一種方式的副本?通過值或參考,在C++構造函數,需要存儲的副本?

這是我能想到的最短例如:

struct foo { 
    bar _b; 
    foo(bar [const&] b) // pass by value or reference-to-const? 
     : _b(b) { } 
}; 

這裏的想法是,我想對酒吧的拷貝構造函數的調用最小化創建一個Foo對象時,在任何的各種方式foo對象可能被創建。

請注意,我確實知道一些關於複製elision和(命名)返回值優化的內容,並且我已閱讀"Want Speed? Pass by Value",但我不認爲該文章直接解決了此用例。

編輯:我應該更具體。

假設我不知道sizeof(bar),或者判斷bar是根本,內置型(bar可能是一個模板參數,並foo可能是一個類模板,而不是一類)。另外,不要以爲foo的構造函數可以被內聯(或bar的,對於這個問題)。假設我至少可能使用實現RVO的編譯器。

我想什麼是那裏是一種可能性(因爲編譯器優化),像這樣的通話將調用沒有調用bar「(S初始化列表S甚至執行在foo_b(b)時)拷貝構造任何」:

foo f = function_that_creates_and_returns_a_bar_object_using_rvo(); 

是否有可能(給定C++ 98標準),這是可以完成的,並且如果是這樣,是它或多或少可能如果foo工作接受它的參數通過引用給const代替按價值?

回答

1

在C++ 98和C++ 03,你應該通過const& bar,然後複製。在C++ 0x中,你應該通過bar然後做一個動作(假設bar有一個移動構造函數)。

#include <utility> 

struct foo 
{ 
    bar _b; 

    foo(bar b) : _b(std::move(b)) {} 
}; 

如果您構建富與左值參數,拷貝構造函數會被調用,以創建一個副本b,並且該副本將被轉移到_b。如果使用右值參數構造foo,bar的移動構造函數將被調用以移入b,然後它將再次移動到_b

+0

我正在考慮這個答案,因爲它是最有前途的,並且它展示了這是C++ 98/03中的一個缺陷,它將由C++ 0x解決。但是,請參閱Alexey Malistov的回答(以及以下評論),以瞭解您可能希望通過C++ 98/03的價值傳遞的情況。 – 2009-12-09 13:56:40

-1

持shared_pointer在你的類吧,並將它傳遞這樣。這樣你永遠不會調用複製構造函數:)。

+0

將調用shared_pointer的拷貝構造函數。 – tstenner 2009-12-04 17:29:17

+0

這是一個非常輕的重量...擔心這將是一個過早優化的情況下,如果我曾聽說過一個。我只是試圖提供一個不同的方法:) – Patrick 2009-12-07 09:01:16

4

看這question

使用const T & arg如果sizeof(T)>sizeof(void*)並且使用T arg如果sizeof(T) <= sizeof(void*)。所有基本類型應該是例外

+0

雖然我認爲這是一個很好的經驗法則,它似乎與參考文章提出的指南(至少有時)衝突... 並在我的具體使用case,foo是一個帶有bar作爲模板參數的類模板,所以我不知道sizeof(bar)(也許我應該在我的示例中包含那個:()。 – 2009-12-04 16:14:27

+0

使用type traits根據sizeof選擇引用或值(bar) – 2009-12-04 17:07:38

+0

@Alexey:除了函子和迭代器(根據Meyers),它應該總是有價值的傳遞,然後我們希望編譯器做的是正確的事情。不知道梅耶斯抽了什麼來得出這個結論。 – 2009-12-04 17:10:46

1

我假設bar的形式爲bar(const bar &b)

取一個const參考這裏的一個正常的拷貝構造函數。 bar的構造函數將採用該引用並執行復制。總副本數:1.

如果您將引用關閉,編譯器會複製b,將副本傳遞給foo的構造函數,然後將其傳遞給bar並複製該副本。

5

所有的事情都是平等的,我通過const &來獲得足夠複雜的類,以及POD和簡單對象的值。

擺出優點/缺點通過const引用,而不是傳統的通通過值傳遞

肯定:

  • 避免了複製(用於對象大加有一個昂貴的複製)
  • 讀 - 只訪問

否定:

  • 有人能的const投const的斷參考,如果他們真的想

更重要的是與陽性是你明確地控制當複製操作(在你的情況下,通過在初始化列表初始化_b時)。考慮消極因素......我同意這是一種風險。我認爲幾乎所有優秀的程序員都會對const_cast的運用感到骯髒。此外,您可以忠實地搜索const_cast,並將番茄扔給參與討論的人。但是,嘿,你永遠不知道誰有時間看像鷹一樣的代碼:)?

我主觀的看法是,在足夠複雜類和環境中的性能事項避免拷貝構造函數大於風險的好處。然而,對於真正的啞巴和POD課程,我傾向於複製數據並通過價值傳遞。

0

老實說,對於這種情況,最好的選擇是讓你的構造函數inline,只要bar的構造函數不拋出。然後通過參考傳遞。

另外,正如您懷疑的那樣,您的文章不適用於此處。

2

從文體上講,我會說通過引用傳遞是更好的方法。

如果表現真的很重要,那麼不要猜測。衡量它。

+1

+1用於推薦測量性能而不是猜測。 – Void 2009-12-04 18:09:49

2

我沒有檢查的標準說什麼,而是試圖通過這個經驗,我可以告訴你,GCC不優化複製走,不管構造是否接受通過值或const引用的論據。

然而,如果構造函數接受一個const引用,它管理當您從現有酒吧對象創建,以避免不必要的副本。

總結:

Bar b = makeBar();   // No copy, because of RVO 
FooByValue f1 = makeBar(); // Copy constructor of Bar called once 
FooByRef f2 = makeBar(); // Copy constructor of Bar called once 
FooByValue f3(b);   // Copy constructor of Bar called twice 
FooByRef f4(b);   // Copy constructor of Bar called only once 

不,我是一個編譯器專家,但我想這是有道理的,它一般不能RVO返回值到任意位置(如的成員字段foo object)。相反,它要求目標位於堆棧頂部。