c++ 聚合是隐式生存期的吗?似乎不对

vu8f3i0k  于 2023-03-14  发布在  其他
关注(0)|答案(1)|浏览(118)

根据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";
}

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

km0tfn4u

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子对象的生存期,例如:

std::construct_at(&p->s);

之后,您可以执行p->s = "abc";

相关问题