pla*_*cel 12 c++ unions c++11 c++14 c++17
给定struct B,它从以下位置继承匿名union数据成员struct A:
#include <cstddef>
struct A
{
union
{
struct { int a, b, c; };
int vals[3];
};
};
struct B: A
{
constexpr B(int x)
:
A{{{ x, x, x }}}
{}
constexpr int& operator[](size_t i)
{
return this->vals[i];
}
constexpr const int& operator[](size_t i) const
{
return this->vals[i];
}
};
Run Code Online (Sandbox Code Playgroud)
我声明一个constexpr类型的变量B,然后调用它将operator[]返回值赋给constexpr int:
int main()
{
constexpr B b(7);
constexpr int i = b[2];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但Clang 3.8给出了错误信息
constexpr variable 'i' must be initialized by a constant expression
Run Code Online (Sandbox Code Playgroud)
这个问题与匿名有关union,因为当我只是使用时,int vals[3]一切正常.
constexpr我缺少C++ 14 限制吗?
这是不允许的:
constexpr int i = b[2];
Run Code Online (Sandbox Code Playgroud)
b[2] 不是常量表达式,因为它包含(ref:N4140 [expr.const] /2.8)
左值转换(4.1)或修改(5.17,5.2.6,5.3.2),应用于引用联合或其子对象的非活动成员的glvalue;
union的活动成员是struct,因为您初始化了该成员.int数组处于非活动状态.
如果您将operator[]函数更改为switch并返回结构的成员,则应该可以正常工作.
注意:访问非活动成员会导致未定义的行为.虽然常见的编译器支持将联合别名作为扩展,但如果您可以将代码设计为不使用联合别名,则可以避免一些麻烦.
匿名结构及其初始化程序也存在问题.具体来说,[class.union]/5:
表单
union { member-specification } ;的联合称为匿名联合 ; 它定义了一个未命名类型的未命名对象.匿名联合的成员规范只应定义非静态数据成员.[注意:无法在匿名联合中声明嵌套类型,匿名联合和函数. - 尾注]
所以你不能在匿名联盟中拥有匿名结构.你需要使其中一个非匿名.例如:
struct A
{
struct AU { int a, b, c; };
union
{
AU a;
int vals[3];
};
};
Run Code Online (Sandbox Code Playgroud)
它适用于初始化程序: A({x, x, x}).A您看到的初始化程序周围的不一致行为可能是gcc错误.
除了 MM 的回答,根据C++ 联合初始化规则,聚合初始化器始终只初始化第一个联合成员,该成员成为该联合的活动成员。
因此更改A为 中int vals[3]的第一个声明union:
struct A
{
union
{
int vals[3];
struct { int a, b, c; };
};
};
Run Code Online (Sandbox Code Playgroud)
或者定义一个初始化成员的构造函数,int vals[3]而不是初始化第一个union成员的聚合初始化:
struct A
{
A(int a, int b, int c)
: vals{ a, b c }
{}
union
{
struct { int a, b, c; };
int vals[3];
};
};
Run Code Online (Sandbox Code Playgroud)
解决了在表达式中读取匿名union成员的问题。int vals[3]constexpr