根据this,聚合是隐式生存期。
类S是隐式生存期类,如果它是一个聚合或至少有一个平凡的合格构造函数和一个平凡的、未删除的析构函数。
隐式生存期对象可以用malloc创建,见this示例。
#include <cstdlib>
struct X { int a, b; };
X* MakeX()
{
// One of possible defined behaviors:
// the call to std::malloc implicitly creates an object of type X
// and its subobjects a and b, and returns a pointer to that X object
X* p = static_cast<X*>(std::malloc(sizeof(X)));
p->a = 1;
p->b = 2;
return p;
}
但是struct A {std::string s;};
也是一个聚合,但是这会产生一个异常,正如我所预料的,因为对s的赋值首先会析构s,而s是无效的,因为它从未被构造过。
#include <cstdlib>
#include <string>
#include <iostream>
struct X { std::string s; };
X* MakeX()
{
X* p = static_cast<X*>(std::malloc(sizeof(X)));
p->s = "abc";
return p;
}
int main()
{
static_assert(std::is_aggregate_v<X>);
auto x = MakeX();
std::cout << x->s << "\n";
}
那么,为什么聚合被认为是隐式生存期类型呢?
1条答案
按热度按时间km0tfn4u1#
是的,因为聚合
X
是隐式生存期类型(参见[class.prop]/9),但重要的是它的成员s
* 不是 * 隐式生存期类型(参见同一参考文献,例如,因为std::string
有一个非平凡析构函数)。结果是
std::malloc(sizeof(X))
将隐式地创建X
对象并开始其生存期,但是它将 * 不 * 开始该X
对象的s
子对象的生存期。这遵循[intro.object]/10,该[intro.object]/10指定它开始隐式生存期对象的生存期,但是没有指定任何关于非隐式生存期(子)对象的内容。在标准中也没有其他内容会像例如构造函数调用和聚合初始化的规则那样指定这些子对象的生存期的开始。因此,
p->s = "abc";
仍然会导致在对象生存期之外访问对象的未定义行为。必须在
malloc
之后首先显式启动s
子对象的生存期,例如:之后,您可以执行
p->s = "abc";
。