2014-11-01 46 views
12

關於Why can't I initialise an array of objects if they have private copy constructors?的現有問題明確指向C++ 03。我知道從這個問題,我想要做什麼是不允許在C + + 03中,但我認爲它應該有可能在C + + 11我有一個不可移動的類(稱爲孩子)和我需要在另一個類的構造函數中初始化一個Child數組(稱之爲Parent)。通過「不可移動」,我的意思是一個Child對象的地址必須在該對象的生命週期中保持不變。什麼是正確的方法來做到這一點?如何用已刪除的拷貝構造函數初始化類的數組(C++ 11)

用C++ 11我已經試過如下:

class Child 
{ 
public: 
    Child (int x) {} 
    ~Child() {} 

    Child (const Child &) = delete; 
}; 

class Parent 
{ 
public: 
    Parent() : children {{5}, {7}} {} 

private: 
    Child children[2]; 
}; 

此代碼編譯罰款鏗鏘3.5.0,GCC卻4.9.1抱怨說,我試圖使用已刪除拷貝構造函數:

test.cc: In constructor ‘Parent::Parent()’: 
test.cc:13:35: error: use of deleted function ‘Child::Child(const Child&)’ 
    Parent() : children {{5}, {7}} {} 
           ^
test.cc:7:5: note: declared here 
    Child (const Child &) = delete; 
    ^

我讀過有關複製初始化和直接初始化(herehere,例如)之間的區別,我想避免調用使用直接初始化拷貝構造函數。我的語法錯了嗎?這是GCC中的錯誤嗎?或者我正在努力做的事情不可能?

+1

在我看來,這是一個叮叮聲蟲,而不是一個海灣合作委員會的錯誤。如果將clang更改爲「children {Child {5},Child {7}}」,clang就無法編譯代碼,該代碼的行爲應與您發佈的內容相同。解決方法是使用「vector」並放置「Child」對象。 – Praetorian 2014-11-01 02:23:04

+0

g ++成功使用'Child children [2] {{5},{7}};'應該與ctor初始化程序列表中出現相同初始化程序的版本相同;都覆蓋了[dcl.init.list]/3 – 2014-11-01 03:04:13

+1

閱讀初始化部分,這段代碼似乎是正確的; 'children [2] = {{5},{7}}''表示'children [0]'是從'{5}'複製初始化的,也就是'Child c = {5};'並且由[dcl.init.list]再次覆蓋,它調用'c'的構造函數,它需要'int'(不涉及副本)。 – 2014-11-01 03:11:25

回答

3

我同意評論,這似乎是一個GCC錯誤(報告爲63707)。

它只有在數組中的類型具有用戶定義的析構函數時纔會編譯,這對我沒有意義。

+0

有時它也失敗,沒有析構:http://stackoverflow.com/questions/31906483/class-member-array-of-objects-without-default-constructor-and-deleted-copy-cont – peku33 2015-08-09 17:34:01

+0

@ peku33,是的,我在GCC錯誤報告中給出了一個類似的例子。這個問題發生在非平凡的析構函數上,而不僅僅是用戶定義的問題。 – 2015-08-11 14:19:05

-1

我經歷了類似的問題來了,即這個代碼

#include <iostream> 

class Widget { 
public: 
    Widget(int i) { std::cout << "Ctor " << i << std::endl; } 

    Widget(const Widget&); // = delete; 
}; 

int main() { 
    Widget w = 123; 
} 

編譯,得到所需要的結果,但在取消對= delete後未能用gcc-4.9編譯。

在閱讀標準I 認爲答案在於8.5/16中的最高縮進的第二項,其在下面引用。

基本上什麼似乎發生的是,編譯器概念要創建一個臨時Widget類型和的直接初始化從臨時實際對象w通過拷貝構造函數。由於複製構造函數被刪除,編譯停止。如果複製構造函數沒有被刪除,編譯器以後會意識到它可能會隱藏副本,但是它不會那麼遠。

下面是相關部分:

[...]爲[...]副本初始化例[...],可以從源類型轉換爲用戶定義的轉換序列目的地類型如13.3.1.4所述進行枚舉,最好的是通過重載分辨率(13.3)選擇。 [...]所選函數以初始化表達式作爲參數來調用;如果該函數是構造函數,則該調用會初始化目標類型的cv未限定版本的臨時值。 [...]調用的結果(這是構造函數的臨時情況)用於根據上面的規則直接初始化作爲複製初始化目標的對象。在某些情況下,通過將中間結果直接構造到被初始化的對象中,允許實現消除該直接初始化中固有的複製;見12.2,12.8。

但我可能是錯的,因爲[...]部件中有很多我不明白。

+3

我不認爲這是真正相關的。 'Widget w = 123;'使用複製構造函數Widget w {123};'是直接初始化。 – 2014-11-17 00:46:55

+1

這絕對是一種不同的情況。在堆棧上創建一個'Widget'需要一個可訪問的析構函數,因爲它會自動調用堆棧對象。如果你有一個被刪除的析構函數,你不能在堆棧上創建它。這與問題無關。 – 2015-01-28 11:40:30

+0

@JonathanWakely它不是我的例子中刪除的析構函數。 – 2015-01-28 15:22:41