聚合是隐式生命周期?好像不太对

问题描述 投票:0回答:1

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

但是

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

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

c++ c++20 lifetime implicit-lifetime
1个回答
3
投票

是的,作为聚合

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

结果是

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

因此

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

您必须在

s
之后首先显式地开始
malloc
子对象的生命周期,例如与:

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

在那之后,你可以做

p->s = "abc";
.

© www.soinside.com 2019 - 2024. All rights reserved.