TL;DR
聚合初始化可用于延长临时的、用户定义的构造函数不能做同样的事情,因为它实际上是一个函数调用。
Note: Both T const&
and T&&
apply in the case of aggregate-initalization and extending the life of temporaries bound to them.
什么是总计的?
struct S { // (1)
std::vector<int>&& vec;
};
为了回答这个问题,我们必须深入研究初始化和初始化之间的区别总计的和初始化班级类型,但首先我们必须确定什么是总计的 is:
8.5.1p1
骨料 [dcl.init.aggr]
An 总计的是一个数组或类(第 9 条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第 11 条),没有基类(第 10 条),并且没有虚函数(10.3)
Note: The above means that (1) is an aggregate.
How are 骨料初始化?
之间的初始化总计的 and a "非聚合” 差异很大,这是直接来自标准的另一部分:
8.5.1p2
骨料 [dcl.init.aggr]
当聚合由初始化列表初始化时,如 8.5.4 中所指定,初始值设定项列表的元素被视为聚合成员的初始值设定项,按递增的下标或成员顺序。每个成员都是复制初始化从相应的初始化子句.
上面的引用表明我们正在初始化我们的成员总计的与初始化程序初始化子句,中间没有步骤。
struct A { std::string a; int b; };
A x { std::string {"abc"}, 2 };
从语义上讲,上面的内容相当于使用下面的内容初始化我们的成员,只是A::a
and A::b
在这种情况下只能通过x.a
and x.b
.
std::string A::a { std::string {"abc"} };
int A::b { 2 };
如果我们改变类型A::a
到右值引用,或const 左值引用, 我们将directly将用于初始化的临时用途绑定到x.a
.
的规则右值引用, and const 左值引用,表示临时对象的生命周期将延长到宿主的生命周期,这正是将要发生的情况。
使用用户声明的构造函数进行初始化有何不同?
struct S { // (2)
std::vector<int>&& vec;
S(std::vector<int>&& v)
: vec{std::move(v)} // bind to the temporary provided
{ }
};
A 构造函数实际上只不过是一个奇特的函数,用于初始化class实例。适用于函数的规则也适用于它们。
在延长临时修复体的使用寿命方面没有什么区别。
std::string&& func (std::string&& ref) {
return std::move (ref);
}
临时传递给func
不会仅仅因为我们有一个参数声明为右值/左值引用而延长其生命周期。即使我们返回"same"参考,以便它可以在外部使用func
,这不会发生。
这就是在构造函数中发生的事情(2),毕竟一个构造函数只是一个“奇特的功能" 用于初始化一个对象。
12.2p5
临时对象 [class.temporary]
引用绑定到的临时对象或引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在,但以下情况除外:
构造函数构造函数初始化程序 (12.6.2) 中引用成员的临时绑定将持续存在,直到构造函数退出。
函数调用 (5.2.2) 中对引用参数的临时绑定将持续存在,直到包含调用的完整表达式完成为止。
-
函数返回语句 (6.6.3) 中绑定到返回值的临时变量的生命周期不会延长;临时值在 return 语句中完整表达式的末尾被销毁。
- 临时绑定到 a 中的引用新的初始化器(5.3.4) 一直持续到包含以下内容的完整表达式完成新的初始化器.
Note: Do note that aggregate initialization through a new T { ... }
differ from the previously mentioned rules.