簡體   English   中英

為什么在這種情況下不調用復制構造函數?

[英]Why copy constructor is not called in this case?

這是小代碼段:

class A
{
public:
    A(int value) : value_(value)
    {
        cout <<"Regular constructor" <<endl;
    }

    A(const A& other)   : value_(other.value_)  
    {
        cout <<"Copy constructor" <<endl;
    }

private:
    int value_;
};
int main()
{
    A a = A(5);
}

我假設輸出將是“常規構造函數”(對於RHS),然后是“復制構造函數”,對於LHS。 因此,我避免使用這種樣式,並始終將類的變量聲明為A a(5); 但是令我驚訝的是,從未調用過復制構造函數的代碼(Visual C ++ 2008)

是否有人知道此行為是編譯器優化的結果,還是C ++的某些已記錄(可移植)功能? 謝謝。

從另一個評論:“所以,在默認情況下,我不應該依賴它(因為它可能取決於編譯器)”

不,實際上,它並不依賴於編譯器。 任何值得一沙的編譯器都不會浪費時間來構造A,然后將其復制過來。

在標准中,它明確表示T = x;是完全可以接受的T = x; 相當於說T(x); (§12.8.15,第211頁)用這樣做T(T(x))顯然是多余的,所以它消除了內T

為了獲得所需的行為,您將強制編譯器默認構造第一個A:

A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);

我正在研究此問題,以回答另一個被騙的問題,因此為了不浪費工作,我正在回答這個問題。

A A a = A(5)形式的語句稱為變量a 復制初始化 C ++ 11標准8.5 / 16指出:

所選函數以初始化器表達式作為參數進行調用; 如果函數是構造函數,則調用將初始化目標類型的cv不合格版本的臨時版本。 臨時變量是一個prvalue。 然后,根據上面的規則,調用的結果(對於構造方法而言是臨時的)將用於直接初始化作為復制初始化目標的對象。 在某些情況下,允許通過將中間結果直接構造到要初始化的對象中來消除直接初始化中固有的復制。 參見12.2,12.8

這意味着,編譯器查找適當的構造函數來處理A(5)創建一個臨時並復制到臨時a 但是在什么情況下可以刪除副本?

讓我們看看12.8 / 31的內容:

當滿足某些條件時,即使該對象的復制/移動構造函數和/或析構函數具有副作用,也允許實現忽略類對象的復制/移動構造。 在這種情況下,實現將忽略的復制/移動操作的源和目標視為引用同一對象的兩種不同方式,並且該對象的銷毀發生在兩個對象本來應該以較晚的時間發生。沒有優化就銷毀。 在以下情況下允許復制/移動操作的這種省略,稱為復制刪除(可以合並以消除多個副本):

[...]

  • 當尚未綁定到引用(12.2)的臨時類對象將被復制/移動到具有相同cv-unqualtype類型的類對象時,可以通過將臨時對象直接構造到目標中來省略復制/移動操作省略的副本/移動的

考慮到所有這些,這就是表達式A a = A(5)

  1. 編譯器會看到帶有復制初始化的聲明
  2. 選擇A(int)構造函數以初始化臨時對象
  3. 因為臨時對象綁定到的引用,它具有相同類型的A作為復制初始化表達目標類型時,編譯器允許直接構造對象到a ,eliding臨時

在這里,您從臨時A(5) 復制a 副本 根據C ++標准12.2 / 2,此處的實現允許跳過調用副本構造函數。

A a = A(5);

這條線相當於

A a(5);

盡管具有函數式外觀,但第一行僅使用參數5構造a 。不涉及復制或臨時操作。 根據C ++標准的12.1.11節:

功能符號類型轉換(5.2.3)可用於創建其類型的新對象。 [注意:語法看起來像是對構造函數的顯式調用。 —尾注]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM