更改常量表达式中联合的活动成员

Changing active member of union in constant expressions

本文关键字:活动 成员 常量 表达式      更新时间:2023-10-16

使用constexprunion时,我发现在constexpr中无法更改union的活动成员。只有一个例外:空类的union

constexpr bool t()
{
    struct A {};
    struct B {};
    union U { A a; B b; } u{};
    u.a = A{};
    u.b = B{};
    return true;
}
static_assert(t());
constexpr bool f()
{
    struct A { char c; };
    struct B { char c; };
    union U { A a; B b; } u{};
    u.a = A{};
    u.b = B{}; // error originating from here
    return true;
}
static_assert(f());

第一个函数可以产生常量表达式。但第二个不能。硬错误显示:

main.cpp:23:15: error: static_assert expression is not an integral constant expression
static_assert(f());
              ^~~
main.cpp:20:11: note: assignment to member 'b' of union with active member 'a' is not allowed in a constant expression
    u.b = B{};
          ^
main.cpp:20:9: note: in call to '&u.b->operator=(B{})'
    u.b = B{};
        ^
main.cpp:23:15: note: in call to 'f()'
static_assert(f());
              ^
1 error generated.

实例

1.)是否可以在常量表达式中更改union的活动成员?

我试图销毁活动成员,但这是不允许的,因为销毁程序通常不是constexpr。此外,我尝试使用放置operator new::new (&u.b) B{2};),但也未成功。常量表达式中也不允许使用reinterpret_cast。更改公共初始子序列的成员也被禁止。

2.)有没有办法使可变的(在改变主动替换类型的意义上)字面上类似于boost::variant的类型?如果可能的话,它的存储空间看起来怎么样?

3.)在运行时向union的非活动成员分配平凡复制可分配类型是未定义的行为吗?使用放置operator new来构造普通可复制类型的union的非活动成员,以避免在运行时对活动成员进行初步破坏,这是否是未定义的行为

附加:

我可以更改整个文字类型union,但不能更改其非活动成员:

constexpr
bool
f()
{
    struct A { char c; };
    struct B { char c; };
    union U 
    {
        A a; B b; 
        constexpr U(A _a) : a(_a) { ; }  
        constexpr U(B _b) : b(_b) { ; }  
    };
    U a(A{});
    a.a = A{}; // check active member is A
    U b(B{});
    b.b = B{}; // check active member is B
    a = b;
    a = B{}; // active member is B!
    return true;
}
static_assert(f());

实例

因此,对于普通可复制类型的文字类型variant,转换赋值运算符将是template< typename T > constexpr variant & operator = (T && x) { return *this = variant(std::forward< T >(x)); }

免责声明:"活动"在P0137R0中定义。

有可能在常量中更改工会的活跃成员吗表达式?

不直接,因为禁止修改非活动成员-[expr.const]/(2.8):

--左值到右值的转换(4.1)或修改(5.18、5.2.6、5.2.7),5.3.2)应用于glvalue,该glvalue指的是并集的非活动成员或其子对象;

然而,这种措辞似乎有缺陷,因为确实可以通过分配另一个工会对象来"修改"非活跃成员,如您的示例所示。事实上,拷贝分配操作符执行底层字节和活动成员内部信息的拷贝:

联合X副本的隐式定义的副本分配运算符CCD_ 19的对象表示(3.9)。


分配给的非活动成员是未定义的行为吗运行时平凡可复制可赋值类型的并集?

这可能对普通可复制类类型的对象来说很好,因为它们有普通的析构函数和复制构造函数/赋值运算符。虽然没有具体说明,但CWG#1116似乎暗示了它的意图:

我们从不说工会的积极成员是什么,它是如何成为的等等。该标准没有明确以下内容有效:

union U { int a; short b; } u = { 0 };
int x = u.a; // presumably this is OK, but we never say that a is the active member
u.b = 0;     // not clear whether this is valid