声明 empty/default 构造函数的几种方法之间的区别
Differences between several ways to declare an empty/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++ 标准产生完全相同的行为。
这些都产生相同的行为,但在编译时几乎没有区别。
第一个和第二个很简单所以..从第三个开始
当您使用构造函数时
class C3 {
constexpr C3() noexcept = default;
int* ptr;
int val;
}
通过使用 constexpr 值在任何情况下都不会改变我们也可以使用 cont 并且通过使用 noexcept 我们正在检查它是否 returns 在编译时是否为真理解)
class C6 {
constexpr C6() noexcept : ptr{nullptr}, val{0} {}
int* ptr;
int val;
};
如果我们谈论它,那么我们正在进行成员初始化,这非常快,因为编译时间节省了几毫秒,这对于大型程序来说是巨大的。
我可以说这是一个小差异希望它会很有用。
上面的部分代码是非法的,无法通过编译。我将检查错误并解释错误原因。
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{};
}
值得注意的是,some_c10
的非 constexpr
版本将在运行时调用完全相同的 constexpr
版本的构造函数,除非非 [=17] =] 指定版本,而此版本将在编译时评估 constexpr
构造函数。这里的 constexpr
-context 显然是使用 constexpr
关键字创建的;省略它将导致非 constexpr
版本,除非聪明的编译器选择声明它 constexpr
无论如何。
此外,第一个class会隐式创建一个平凡的构造函数,也就是说,构造函数不会执行任何操作并保持值未初始化(这里没关系:它会创建一个平凡的构造函数,但由于给定的类型是 classes,它们具有非静态私有成员,这会阻止编译器定义这样的构造函数。尽管将 C1 声明为 struct
会起作用。
除此之外,你发现的各种拼写真的没有区别。它主要只是表达完全相同事物的另一种方式。尽管在几乎所有情况下,函数的外联定义应该是首选解决方案。
class C1 {
int* ptr;
int val;
}
编译将声明并定义一个 public 普通 noexcept
默认构造函数。由于微不足道,它不会对成员执行 any 初始化。
用户可以在默认初始化(不会执行任何数据成员初始化)或值初始化(将对数据成员进行零初始化)之间进行选择:
C1 x; // default-initialized
C1 y = C1(); // value-initialized
一个普通的默认 ctor 会影响对象的生命周期和 class 的 PODness。
如果 C1
是一个 struct
(即数据成员是 public),它将是一个聚合:
C1 a {nullptr, 42};
C1 z{}; // aggregate-initialized, but same effects as for y
尽管默认构造函数不是 constexpr
,但您可以在常量表达式中创建此 class 的实例:很简单,默认构造函数不会在值初始化中调用(也不会在聚合初始化)。
class C2 {
int* ptr = nullptr;
int val = 0;
};
编译器将声明并定义一个public constexpr
和noexcept
默认构造函数,它根据数据成员的NSDMI(非静态数据成员初始化器,即= x;
)。默认初始化和值初始化都将调用默认构造函数并初始化成员。根据 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;
由于构造函数在第一次声明时没有默认,这是一个(私有)用户提供的默认构造函数。因此,与 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
函数是隐式内联的。
在 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++ 标准产生完全相同的行为。
这些都产生相同的行为,但在编译时几乎没有区别。 第一个和第二个很简单所以..从第三个开始 当您使用构造函数时
class C3 {
constexpr C3() noexcept = default;
int* ptr;
int val;
}
通过使用 constexpr 值在任何情况下都不会改变我们也可以使用 cont 并且通过使用 noexcept 我们正在检查它是否 returns 在编译时是否为真理解)
class C6 {
constexpr C6() noexcept : ptr{nullptr}, val{0} {}
int* ptr;
int val;
};
如果我们谈论它,那么我们正在进行成员初始化,这非常快,因为编译时间节省了几毫秒,这对于大型程序来说是巨大的。 我可以说这是一个小差异希望它会很有用。
上面的部分代码是非法的,无法通过编译。我将检查错误并解释错误原因。
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{};
}
值得注意的是,some_c10
的非 constexpr
版本将在运行时调用完全相同的 constexpr
版本的构造函数,除非非 [=17] =] 指定版本,而此版本将在编译时评估 constexpr
构造函数。这里的 constexpr
-context 显然是使用 constexpr
关键字创建的;省略它将导致非 constexpr
版本,除非聪明的编译器选择声明它 constexpr
无论如何。
此外,第一个class会隐式创建一个平凡的构造函数,也就是说,构造函数不会执行任何操作并保持值未初始化(这里没关系:它会创建一个平凡的构造函数,但由于给定的类型是 classes,它们具有非静态私有成员,这会阻止编译器定义这样的构造函数。尽管将 C1 声明为 struct
会起作用。
除此之外,你发现的各种拼写真的没有区别。它主要只是表达完全相同事物的另一种方式。尽管在几乎所有情况下,函数的外联定义应该是首选解决方案。
class C1 {
int* ptr;
int val;
}
编译将声明并定义一个 public 普通 noexcept
默认构造函数。由于微不足道,它不会对成员执行 any 初始化。
用户可以在默认初始化(不会执行任何数据成员初始化)或值初始化(将对数据成员进行零初始化)之间进行选择:
C1 x; // default-initialized
C1 y = C1(); // value-initialized
一个普通的默认 ctor 会影响对象的生命周期和 class 的 PODness。
如果 C1
是一个 struct
(即数据成员是 public),它将是一个聚合:
C1 a {nullptr, 42};
C1 z{}; // aggregate-initialized, but same effects as for y
尽管默认构造函数不是 constexpr
,但您可以在常量表达式中创建此 class 的实例:很简单,默认构造函数不会在值初始化中调用(也不会在聚合初始化)。
class C2 {
int* ptr = nullptr;
int val = 0;
};
编译器将声明并定义一个public constexpr
和noexcept
默认构造函数,它根据数据成员的NSDMI(非静态数据成员初始化器,即= x;
)。默认初始化和值初始化都将调用默认构造函数并初始化成员。根据 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;
由于构造函数在第一次声明时没有默认,这是一个(私有)用户提供的默认构造函数。因此,与 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
函数是隐式内联的。