C++ C++;无法将联合成员对象指定给后期构造

C++ C++;无法将联合成员对象指定给后期构造,c++,c++17,C++,C++17,下面的测试代码(编译的-std=c++17)非常简单。在这种情况下,在构造函数运行之后,对联合的任何成员进行赋值似乎什么都不做。我实例化一个联合,它包含父类成员和子类成员,然后分配给子成员。我尝试了几个联合构造函数(编号1-4)。我在每一步后打印原始内存,但它从未改变 分配u.child=assign\u child后,原始内存不变。使用构造函数#3,成员应初始化为父类类,因此推测这应该被子类覆盖,并且是不同的 使用空构造函数#1时,使用u.Ptr()->Print()segfaults通过指

下面的测试代码(编译的-std=c++17)非常简单。在这种情况下,在构造函数运行之后,对联合的任何成员进行赋值似乎什么都不做。我实例化一个
联合
,它包含
父类成员和
子类成员,然后分配给子成员。我尝试了几个
联合
构造函数(编号1-4)。我在每一步后打印原始内存,但它从未改变

  • 分配
    u.child=assign\u child后,原始内存不变。使用构造函数#3,成员应初始化为
    父类
    类,因此推测这应该被子类覆盖,并且是不同的

  • 使用空构造函数#1时,使用
    u.Ptr()->Print()
    segfaults通过指针访问调用
    Print()
    ,但直接通过成员访问调用时效果良好。为什么呢?我想这意味着要么G++在该位置用字节编码对象类型,要么编译器根据使用的构造函数选择不同的代码路径

  • 如果我在
    Union
    构造函数中初始化父成员或子成员,
    ptr->Print()
    使用该类型。这必须意味着a)编译器跟踪使用的构造函数,然后根据代码分析调用匹配类型,或者b)对象类型在该位置的字节中编码。哪个是真的

  • #包括
    使用名称空间std;
    类父{
    公众:
    虚拟空打印(){
    
    CUT

    也许不是一个解释,确切地说是什么,但是你正在尝试的东西并没有被C++标准所覆盖(它是未定义的行为)。 原则上,您只能使用(->读取/分配)已构造的联合成员。如果要激活另一个成员,则必须使用联合手动构造和销毁成员

    在您的示例中,您构造了
    父级
    成员(而不是
    子级
    ),因此您只允许访问
    父级
    (即,不允许访问
    ptr()
    中的
    子级
    ,例如,调用子级函数)

    此规则有一个例外:如果非活动的联合成员是当前活动成员的公共初始序列的一部分,则允许从该成员读取

    如果类
    Parent
    Child
    是标准布局类型,则可以构造
    Child
    联合成员,然后从
    Parent
    联合成员读取


    但不是相反的(如你所尝试的),并且只有(如果!)如果你有标准的布局类型(你没有因为代码>虚拟< /代码>)

    也许不是一个解释到底发生了什么,但是你正在尝试的东西不被C++标准覆盖(它是未定义的行为)。 原则上,您只能使用(->读取/分配)已构造的联合成员。如果要激活另一个成员,则必须使用联合手动构造和销毁成员

    在您的示例中,您构造了
    父级
    成员(而不是
    子级
    ),因此您只允许访问
    父级
    (即,不允许访问
    ptr()
    中的
    子级
    ,例如,调用子级函数)

    此规则有一个例外:如果非活动的联合成员是当前活动成员的公共初始序列的一部分,则允许从该成员读取

    如果类
    Parent
    Child
    是标准布局类型,则可以构造
    Child
    联合成员,然后从
    Parent
    联合成员读取

    但不是相反的方式(正如您尝试的那样),并且只有在您有标准布局类型(由于
    virtual
    ,您没有标准布局类型)的情况下才(!)

    您的构造函数将激活父成员

    您访问联盟的非活动成员。程序的行为未定义。无论您使用哪种构造函数,您都会在示例的某个点访问非活动成员

    回答您的所有问题:程序的行为未定义


    更改活动成员的一种定义明确的方法是首先销毁以前的活动成员:

    parent.~Parent();
    new (&child) Child();
    
    在与派生类的联合中存储父类的单独实例似乎是一种奇怪的设计

    您的构造函数将激活父成员

    您访问联盟的非活动成员。程序的行为未定义。无论您使用哪种构造函数,您都会在示例的某个点访问非活动成员

    回答您的所有问题:程序的行为未定义


    更改活动成员的一种定义明确的方法是首先销毁以前的活动成员:

    parent.~Parent();
    new (&child) Child();
    

    将父类的单独实例存储在具有派生类的联合中似乎是一种奇怪的设计。

    使用
    std::variant
    。使用
    union
    必须手动管理包含的对象,使用placement new等手动管理对象的生命周期。请参见使用
    std::variant
    。使用
    union
    必须手动管理c使用placement new等手动指定包含对象的生存期,请参见您能否详细说明?如果我使用第一个构造函数(无初始化),然后稍后通过
    u.child=assign_child;
    ,u.Ptr()->Print()分配
    child
    不起作用。我不明白,因为该成员刚刚被分配。为什么通过placement new分配与equals分配不同?即使我删除了所有引用并访问了
    parent
    @evo8198,这个问题也会出现。如果您分配了一个非活动的非平凡的union成员,程序的行为是未定义的。唯一的方法是o激活一个非平凡的联盟成员是使用的地方
    u.child.Print();
    
    parent.~Parent();
    new (&child) Child();