声明空/默认构造函数的几种方法之间的差异

时间:2015-10-18 06:27:38

标签: c++ constructor standards c++14 default-constructor

在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 ++标准产生完全相同的行为。

3 个答案:

答案 0 :(得分: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.

除此之外,(有效)代码将生成完全相同的运行时 - 行为;声明constexpr的构造函数,如果在constexpr - 上下文中使用,将在编译时进行评估(这通常是一件好事,因为它节省了一些计算)。一个例子是:

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

值得注意的是,非constexpr版本的some_c10将在运行时调用构造函数的完全相同的constexpr版本,除非非costexpr指定了版本,而此版本将在编译时评估constexpr构造函数。这里的constexpr - 上下文显然是使用constexpr关键字创建的;省略它将导致非constexpr - 版本,除非智能编译器选择声明它constexpr

此外,第一个类将隐式创建一个简单的构造函数,也就是说,构造函数不会执行任何操作并保持值未初始化(在这里没关系:它会创建一个简单的构造函数,但由于给定的类型是类,他们有非静态的私有成员,这会阻止编译器定义这样的构造函数。虽然声明C1 struct会有效。)

除此之外,你发现的各种拼写确实没有区别。它主要是另一种表达完全相同的方式。虽然在几乎所有情况下,函数的外联定义应该是首选解决方案。

答案 1 :(得分:3)

class C1 {
    int* ptr;
    int val;
}

编译将声明并定义一个公共的普通noexcept默认ctor。这是微不足道的,它不会执行成员的任何初始化。 用户可以选择默认初始化(不执行数据成员的任何初始化)或值初始化(将初始化数据成员):

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

普通的默认ctor会影响对象的生命周期和类的POD。

如果C1struct(即数据成员是公开的),那么它将是一个聚合:

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

尽管默认的ctor不是constexpr,但是你可以在常量表达式中创建这个类的实例:由于很简单,默认的ctor不会在值初始化中调用(也不会在聚合初始化中调用)。 / p>

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

编译器将声明并定义一个公共constexprnoexcept默认ctor,它根据NSDMI(非静态数据成员初始值设定项,即= 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;
};

严重违法。你不能只默认函数体,你必须默认整个构造函数。

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

C2相同的效果,除了这是私有ctor,而struct C6将不再是聚合,因为此ctor是用户提供的。

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;

由于ctor在第一次声明时没有默认,因此这是(私人)用户提供的默认ctor。因此,行为与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函数隐式内联。

答案 2 :(得分:0)

这些都产生相同的行为,但在编译时时差别不大。 第一个第一个是如此简单,从第三个开始 当您使用构造函数

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

通过使用constexpr值在任何情况下都不会改变我们也可以使用cont并且通过使用noexcept我们检查它是否在编译时返回true同样为了所有其他(仅用于理解)

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

如果我们谈论它,那么我们正在进行成员初始化这是非常快的,因为编译器时间节省了几毫秒,这对于大型程序来说是巨大的。 这有点不同,我可以说希望它会有用甚至很少。