2015-10-18 38 views
0

在C++ 14有幾種方法來聲明一個空的構造申報空/默認構造函數

class C1 { 
    int* ptr; 
    int val; 
}; 

class C2 { 
    int* ptr = nullptr; 
    int val = 0; 
}; 

class C3 { 
    constexpr C3() noexcept = default; 
    int* ptr; 
    int val; 
}; 

class C4 { 
    constexpr C4() noexcept = default; 
    int* ptr = nullptr; 
    int val = 0; 
}; 

class C5 { 
    constexpr C5() noexcept : ptr{nullptr}, val{0} = default; 
    int* ptr; 
    int val; 
}; 

class C6 { 
    constexpr C6() noexcept : ptr{nullptr}, val{0} {} 
    int* ptr; 
    int val; 
}; 

class C7 { 
    constexpr C7() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C7::C7() noexcept = default; 

class C8 { 
    constexpr C8() noexcept; 
    int* ptr = nullptr; 
    int val = 0; 
}; 
constexpr C8::C8() noexcept = default; 

class C9 { 
    constexpr C9() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C9::C9() noexcept : ptr{nullptr}, val{0} = default; 

class C10 { 
    constexpr C10() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C10::C10() noexcept : ptr{nullptr}, val{0} {} 

我想知道,什麼是所有這些類與之間有什麼確切的差別類是嚴格等價的,並且會根據C++標準生成完全相同的行爲。

+1

C3,C5,C7和C9是非法的。 – dyp

+1

所有這些爲你編譯?如果是這樣,哪個編譯器和哪些選項? – juanchopanza

回答

4
class C1 { 
    int* ptr; 
    int val; 
} 

該編譯將聲明和定義一個公共平凡的noexcept默認ctor。微不足道的,它不會執行任何成員的初始化。 用戶可以缺省初始化之間進行選擇(這將不執行數據成員中的任何INIT)或值初始化(將零初始化數據成員):

C1 x;  // default-initialized 
C1 y = C1(); // value-initialized 

平凡缺省構造函數具有影響對象的生命期和班級的POD。

如果C1是一個struct(即數據成員公職),這將是一個總:

C1 a {nullptr, 42}; 
C1 z{};  // aggregate-initialized, but same effects as for y 

儘管默認的構造函數不是constexpr,您可以創建中不斷此類的實例表達式:不重要的是,默認的ctor不會在值初始化中(也不在集合初始化中)調用。


class C2 { 
    int* ptr = nullptr; 
    int val = 0; 
}; 

編譯器將聲明和定義一個公共constexprnoexcept默認構造函數,它根據其NSDMIs初始化數據成員(非靜態數據成員初始化,即= x;)。默認值和初始值都會調用默認的ctor並初始化成員。根據C++ 14規則,一個struct C2將是一個聚合。


class C3 { 
    constexpr C3() noexcept = default; 
    int* ptr; 
    int val; 
}; 

是非法的,因爲構造函數,編譯器會宣佈自己(見C1)不會constexpr。它不是constexpr,因爲它不初始化所有數據成員。請注意,OP中所有用戶聲明的ctors都是(隱式)私有的。


class C4 { 
    constexpr C4() noexcept = default; 
    int* ptr = nullptr; 
    int val = 0; 
}; 

C2


class C5 { 
    constexpr C5() noexcept : ptr{nullptr}, val{0} = default; 
    int* ptr; 
    int val; 
}; 

Gramatically非法的。你不能只默認函數體,你必須默認整個構造函數。


class C6 { 
    constexpr C6() noexcept : ptr{nullptr}, val{0} {} 
    int* ptr; 
    int val; 
}; 

相同的效果C2,除了這是一個私人的構造函數和struct C6將不再是一個聚合,因爲這是構造函數用戶提供的。


class C7 { 
    constexpr C7() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C7::C7() noexcept = default; 

非法出於同樣的原因爲C3


class C8 { 
    constexpr C8() noexcept; 
    int* ptr = nullptr; 
    int val = 0; 
}; 
constexpr C8::C8() noexcept = default; 

由於構造函數沒有在其第一聲明違約,這是(私人)用戶提供默認構造函數。因此,行爲與C6相同。


class C9 { 
    constexpr C9() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C9::C9() noexcept : ptr{nullptr}, val{0} = default; 

非法出於同樣的原因爲C3


class C10 { 
    constexpr C10() noexcept; 
    int* ptr; 
    int val; 
}; 
constexpr C10::C10() noexcept : ptr{nullptr}, val{0} {} 

C8; constexpr函數隱式內聯。

+1

C2也不是一個聚合體,因爲它由於被聲明爲'class'而不是'struct'而具有私有的非靜態數據成員。除此之外,所有給定的類幾乎都是不可用的,所以讓我們假設它們是'struct's);非常好,徹底的答案呢! – dasdave

+0

@dasdave d'oh!我根本沒有注意到'class'鍵。 – dyp

+0

構造函數的瑣碎性如何影響對象的生命週期? – 0x499602D2

0

這些都產生相同的行爲,但與差別不大編譯時。 的firstand第二個是如此的簡單,所以..從第三個 開始當您使用構造函數

class C3 { 
    constexpr C3() noexcept = default; 
    int* ptr; 
    int val; 
} 

使用constexpr值在任何情況下,我們也可以使用續不改變,並通過使用noexcept我們正在檢查是否返回true,否則在編譯時同樣適用於其他所有的(只是爲了理解)

class C6 { 
    constexpr C6() noexcept : ptr{nullptr}, val{0} {} 
    int* ptr; 
    int val; 
}; 

如果我們談論它,然後我們正在做的成員初始化,這是相當快的編譯時間被保存毫秒級對於大型項目來說非常重要。 這是一個有點差別,我可以告訴希望它會有用甚至很少。

3

一些上面的代碼的代碼是非法的,將無法編譯。我將通過這些錯誤並解釋他們爲什麼是錯誤。

class C3 { 
    constexpr C3() noexcept = default; 
    // A constexpr constructor cannot be defaulted since the default 
    // version of the constructor is not constexpr. In this case, you will 
    // thus have to always explicitly define a constexpr constructor. 
// ... 
}; 

// For C4, on the other hand, defaulting the constexpr will work since 
// you gave default (constant) values for all members. The default 
// constructor will not have to care about them. 

class C5 { 
    constexpr C5() noexcept : ptr{nullptr}, val{0} = default; 
    // Besides this being invalid syntax, you are implicitly defining 
    // a constructor, and then using the default version of it, which 
    // does not make sense. 
// ... 
}; 

// The rest of the errors are just plain reproduction in a slightly 
// amended form. 

除此之外,(有效)的代碼會產生完全相同的運行時 -behavior;構造函數聲明constexpr會,如果在一個constexpr -context使用,在編譯時(這是一件好事,因爲通常可以節省一些計算)進行評估。一個例子是:

constexpr auto some_c10 = C10{}; 
// or 
constexpr void do_something() { // works for constexpr functions as well! yay! 
    auto some_other_c10 = c10{}; 
} 

這是值得注意的是,非constexprsome_c10 -version將調用完全相同constexpr版本的構造函數在運行時,除非指定非costexpr版本,而這個版本將在編譯時評估constexpr構造函數。這裏的constexpr -context顯然是通過使用constexpr關鍵字創建的;省略它會導致非constexpr -version除非一個聰明的編譯器選擇來聲明它constexpr反正。另外,第一個類將隱含地創建一個簡單的構造函數,也就是說,構造函數不會執行任何動作並保持值未初始化(從不在此處:它會創建一個簡單的構造函數,但由於給定的類型是類,他們有非靜態私有成員,這將阻止一個編譯器定義這樣一個構造函數。聲明C1一個struct會雖然工作)。

除此之外,真的是有在你找到了各種各樣的拼寫沒有區別。這主要是表達完全相同的東西的另一種方式。儘管幾乎所有情況下,函數的線外定義都應該是首選解決方案。