2015-10-18 109 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会虽然工作)。

除此之外,真的是有在你找到了各种各样的拼写没有区别。这主要是表达完全相同的东西的另一种方式。尽管几乎所有情况下,函数的线外定义都应该是首选解决方案。