给定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];
}
};
我声明constexpr
类型为B
的变量,然后调用其operator[]
将返回值分配给constexpr int
:
int main()
{
constexpr B b(7);
constexpr int i = b[2];
return 0;
}
然而,Clang 3.8给出了错误信息
constexpr variable 'i' must be initialized by a constant expression
此问题与匿名union
有关,因为当我只使用int vals[3]
时,一切正常。
我是否缺少C ++ 14 constexpr
限制?
答案 0 :(得分:9)
这是不允许的:
constexpr int i = b[2];
b[2]
不是常量表达式,因为它包含(ref:N4140 [expr.const] /2.8)
应用于glvalue的左值到右值转换(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];
};
};
适用于初始值设定项: A({x, x, x})
。您看到的A
初始化程序周围的不一致行为可能是gcc错误。
答案 1 :(得分:4)
除了M.M的回答之外,根据C++ union initialization规则,聚合初始值设定项始终只初始化第一个联合成员,它将成为该联合的活动成员。
将A
改为int vals[3]
union
中的第一个声明:
struct A
{
union
{
int vals[3];
struct { int a, b, c; };
};
};
或定义初始化成员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];
};
};
解决了在union
表达式中阅读匿名int vals[3]
成员constexpr
的问题。