我发现以下方案可以延长临时生命周期,我不知道是否应该,但是可以。
struct S {
std::vector<int>&& vec;
};
int main() {
S s1{std::vector<int>(5)}; // construct with temporary
std::cout << s1.vec[0] << '\n'; // fine, temporary is alive
}
但是,当给S
一个显式值构造函数时,它不再是聚合,并且此方案失败,并且对s1.vec[0]
的读取无效
struct S {
std::vector<int>&& vec;
S(std::vector<int>&& v)
: vec{std::move(v)} // bind to the temporary provided
{ }
};
int main() {
S s1{std::vector<int>(5)}; // construct with temporary
std::cout << s1.vec[0] << '\n'; // not ok. invalid read on free'd memory
}
为什么这对汇总有效?我认为这与构造函数是一个实际的函数调用有关,基于我用const lvalue ref读取的内容。另外,有什么方法可以使后一种情况起作用?
在SO上使用左值引用有很多问题处理类似情况。我看到,如果我使用了const lvalue ref,则无助于延长临时文件的寿命,rvalue refs的规则是否相同?
TL; DR
聚合初始化可用于延长临时对象的寿命,用户定义的构造函数不能做同样的事情,因为它实际上是函数调用。
注:T const&
和T&&
在aggregate-initalization的情况下均适用,并延长了绑定到它们的临时对象的寿命。
struct S { // (1)
std::vector<int>&& vec;
};
要回答这个问题,我们将不得不深入研究aggregate的初始化和class type的初始化之间的区别,但是首先我们必须确定aggregate是什么:]]
:上面的意思是((1)是一个集合。
8.5.1p1
聚合[dcl.init.aggr]
一个aggregate
是一个数组或一个类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),并且没有虚拟功能(10.3)注
aggregate
以上引述表示我们正在使用8.5.1p2
聚合
[dcl.init.aggr]
按8.5.4中的指定,通过初始化程序列表初始化集合时,初始化程序列表的元素被用作集合成员的初始化程序] >>,按递增的下标或成员顺序。每个成员都是从相应的[[initializer-clause
。中的copy-initialized”。
initializer-clause中的初始化程序来初始化
aggregate的成员,两者之间没有任何步骤。
struct A { std::string a; int b; };
A x { std::string {"abc"}, 2 };
在语义上,等效于使用以下内容初始化我们的成员,只是在这种情况下,A::a
和A::b
仅可通过x.a
和x.b
访问。
std::string A::a { std::string {"abc"} }; int A::b { 2 };
如果将将用于初始化的临时用途绑定到A::a
的类型更改为右值引用或const左值引用
,我们将直接
x.a
。rvalue-references]和const lvalue-references]的规则说,临时生存期将延长到主机的生存期,这正是即将发生的事情。
struct S { // (2)
std::vector<int>&& vec;
S(std::vector<int>&& v)
: vec{std::move(v)} // bind to the temporary provided
{ }
};
constructor
class
实例。适用于功能的相同规则也适用于它们。 关于延长临时人员的寿命没有区别。std::string&& func (std::string&& ref) {
return std::move (ref);
}
仅传递给引用,以便它在func
的临时变量,因为我们有一个声明为右值/左值引用的参数,因此不会延长其寿命。即使我们返回“ same”
func
之外可用,也不会发生。这是(2)
的构造函数中发生的事情,毕竟constructor只是用于初始化对象的“ fancy function
”。12.2p5
临时对象
[class.temporary]
引用所绑定的临时对象或引用所绑定的子对象的完整对象的临时对象在引用的生存期内一直存在,但以下情况除外:
与构造函数的ctor-initializer(12.6.2)中的引用成员的临时绑定一直存在,直到构造函数退出。
:请注意,通过- 与函数调用(5.2.2)中的参考参数的临时绑定一直持续到包含该调用的完整表达式完成。
临时绑定到函数return语句(6.6.3)中返回值的生存期;临时在return语句中的全表达式结束时销毁。
- 中的引用的临时绑定一直存在,直到包含
与
new-initializer
- new-initializer的完整表达式完成为止。
注
new T { ... }
进行的聚合初始化与前面提到的规则不同。