S() 与 S{} 之间的区别?
Difference between S() vs S{}?
在下面的代码中,两次赋值有什么区别吗?在这两种情况下, value.v 是否会被默认构造,并且 x 被初始化为 42?
struct S
{
std::vector<int> v;
int x = 42;
};
S value;
void foo()
{
value = S();
value = { };
}
S()
和 S{}
几乎在所有情况下都表示相同的意思。但不是所有个案例。
- 如果
S
不是 class 类型,同样的事情:值初始化。
如果 S
是 class 类型,即 不是 聚合,仍然主要表示同一件事:值初始化。除了以下情况:
struct X { X(std::initializer_list<int>); };
auto x1 = X(); // ill-formed
auto x2 = X{}; // ok, calls constructor
如果 S
是聚合,则 S()
是值初始化,但 S{}
是聚合初始化。即使这在很多时候也意味着同样的事情。但并非所有时候。
示例 1:显式默认构造函数使聚合初始化格式错误
struct A { explicit A(int = 0); };
struct B { A a; };
B b; // OK
B b2 = B(); // OK
B b3{}; // error through trying to copy-list-initialize a = {}
示例 2:某些上下文中的值初始化首先进行零初始化
struct C { C() {} int i; };
struct D { C a; };
D d1{}; // d1.a.i is indeterminate
D d2 = D(); // d2.a.i is zero
虽然在 OP 示例中,S
是一个具有隐式定义的默认构造函数的聚合 - 这是一个有趣的情况。但是在这里,额外的零初始化在语义上没有变化,我们将 x
初始化为 42
并默认构造 v
两种方式。
请注意,同样在 OP 中,这会调用(并且旨在调用)来自 S{}
:
的移动赋值运算符
value = { };
也有可能这会完全调用不同的运算符,因为 {}
最终可能会将 "better" 绑定到不同赋值运算符重载中的某个不同参数。 必须跳过一些挂钩以确保 opt = {}
实际调用移动赋值运算符。
在下面的代码中,两次赋值有什么区别吗?在这两种情况下, value.v 是否会被默认构造,并且 x 被初始化为 42?
struct S
{
std::vector<int> v;
int x = 42;
};
S value;
void foo()
{
value = S();
value = { };
}
S()
和 S{}
几乎在所有情况下都表示相同的意思。但不是所有个案例。
- 如果
S
不是 class 类型,同样的事情:值初始化。 如果
S
是 class 类型,即 不是 聚合,仍然主要表示同一件事:值初始化。除了以下情况:struct X { X(std::initializer_list<int>); }; auto x1 = X(); // ill-formed auto x2 = X{}; // ok, calls constructor
如果
S
是聚合,则S()
是值初始化,但S{}
是聚合初始化。即使这在很多时候也意味着同样的事情。但并非所有时候。
示例 1:显式默认构造函数使聚合初始化格式错误
struct A { explicit A(int = 0); };
struct B { A a; };
B b; // OK
B b2 = B(); // OK
B b3{}; // error through trying to copy-list-initialize a = {}
示例 2:某些上下文中的值初始化首先进行零初始化
struct C { C() {} int i; };
struct D { C a; };
D d1{}; // d1.a.i is indeterminate
D d2 = D(); // d2.a.i is zero
虽然在 OP 示例中,S
是一个具有隐式定义的默认构造函数的聚合 - 这是一个有趣的情况。但是在这里,额外的零初始化在语义上没有变化,我们将 x
初始化为 42
并默认构造 v
两种方式。
请注意,同样在 OP 中,这会调用(并且旨在调用)来自 S{}
:
value = { };
也有可能这会完全调用不同的运算符,因为 {}
最终可能会将 "better" 绑定到不同赋值运算符重载中的某个不同参数。 opt = {}
实际调用移动赋值运算符。