在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 ++标准产生完全相同的行为。
答案 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。
如果C1
是struct
(即数据成员是公开的),那么它将是一个聚合:
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;
};
编译器将声明并定义一个公共constexpr
和noexcept
默认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;
};
如果我们谈论它,那么我们正在进行成员初始化这是非常快的,因为编译器时间节省了几毫秒,这对于大型程序来说是巨大的。 这有点不同,我可以说希望它会有用甚至很少。