在常量表达式中更改联合的活动成员
Changing active member of union in constant expressions
玩 constexpr
和 union
我发现,我无法更改 constexpr
中 union
的活动成员。只有一个例外:union
of empty 类.
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
-like 类型?如果可能的话,它的存储方式如何?
3.) 在运行时 对普通可复制赋值类型 的union
的非活动成员进行赋值是未定义的行为吗?使用放置 operator new
避免活动成员 在运行时初步销毁 ?
附加:
我可以更改整个文字类型 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)); }
。
免责声明:"active" 在 P0137R0 中定义。
Is it possible to change active member of union in constant
expressions?
不直接,因为禁止修改非活跃成员 - [expr.const]/(2.8):
— an lvalue-to-rvalue conversion (4.1) or modification (5.18, 5.2.6,
5.3.2) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
但是,这种措辞似乎有缺陷,因为确实有可能 "modify" 非活动成员通过分配另一个联合对象来实现,如您的示例所示。实际上,复制赋值运算符执行底层字节和有关活动成员的内部信息的复制:
The implicitly-defined copy assignment operator for a union X
copies
the object representation (3.9) of X
.
Is it undefined behaviour to make assignment to non-active members of
union of trivially copy-assignable types at runtime?
这对于平凡可复制的 class 类型的对象来说可能没问题,因为它们具有平凡的析构函数和复制 constructors/assignment 运算符。尽管未指定,CWG #1116 似乎暗示它可以工作:
We never say what the active member of a union is, how it can be
changed, and so on. The Standard doesn't make clear whether the
following is valid:
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
玩 constexpr
和 union
我发现,我无法更改 constexpr
中 union
的活动成员。只有一个例外:union
of empty 类.
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
-like 类型?如果可能的话,它的存储方式如何?
3.) 在运行时 对普通可复制赋值类型 的union
的非活动成员进行赋值是未定义的行为吗?使用放置 operator new
避免活动成员 在运行时初步销毁 ?
附加:
我可以更改整个文字类型 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)); }
。
免责声明:"active" 在 P0137R0 中定义。
Is it possible to change active member of union in constant expressions?
不直接,因为禁止修改非活跃成员 - [expr.const]/(2.8):
— an lvalue-to-rvalue conversion (4.1) or modification (5.18, 5.2.6, 5.3.2) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
但是,这种措辞似乎有缺陷,因为确实有可能 "modify" 非活动成员通过分配另一个联合对象来实现,如您的示例所示。实际上,复制赋值运算符执行底层字节和有关活动成员的内部信息的复制:
The implicitly-defined copy assignment operator for a union
X
copies the object representation (3.9) ofX
.
Is it undefined behaviour to make assignment to non-active members of union of trivially copy-assignable types at runtime?
这对于平凡可复制的 class 类型的对象来说可能没问题,因为它们具有平凡的析构函数和复制 constructors/assignment 运算符。尽管未指定,CWG #1116 似乎暗示它可以工作:
We never say what the active member of a union is, how it can be changed, and so on. The Standard doesn't make clear whether the following is valid:
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