聚合是隐式生命周期?似乎不对

dou*_*oug 8 c++ lifetime c++20 implicit-lifetime

根据thisthis ,聚合是隐式生命周期。

如果类 S 是一个聚合或者至少具有一个普通的合格构造函数和一个普通的、未删除的析构函数,则它是隐式生命周期类。

并且可以使用 malloc 创建隐式生命周期对象。请参阅示例。

#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;
}
Run Code Online (Sandbox Code Playgroud)

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";
}
Run Code Online (Sandbox Code Playgroud)

那么为什么聚合被认为是隐式生命周期类型呢?

use*_*522 4

是的,因为聚合X是隐式生命周期类型(参见[class.prop]/9),但重要的是它的成员s不是式生命周期类型(参见相同的参考,例如因为std::string有一个不平凡的析构函数)。

结果是,std::malloc(sizeof(X))将隐式创建一个X对象并开始其生命周期,但不会启动s该对象的子对象的生命周期X。这遵循[intro.object]/10指定它开始隐式生命周期对象的生命周期,但没有指定有关非隐式生命周期(子)对象的任何内容。标准中也没有其他内容可以指定这些子对象的生命周期的开始,例如构造函数调用和聚合初始化的规则。

因此,p->s = "abc";在其生命周期之外访问对象仍然会导致未定义的行为。

s您必须首先在 后显式启动子对象的生命周期malloc,例如:

std::construct_at(&p->s);
Run Code Online (Sandbox Code Playgroud)

之后,你就可以做p->s = "abc";.