C++ 对匿名结构的变量的constexpr引用
给定的C++ 对匿名结构的变量的constexpr引用,c++,c++11,c++14,unions,c++17,C++,C++11,C++14,Unions,C++17,给定的struct B,它从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&
struct B
,它从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];
}
};
但是,Clang3.8给了我错误消息
constexpr variable 'i' must be initialized by a constant expression
这个问题与匿名联合相关,因为当我简单地使用int-vals[3]
时,一切都正常
是否缺少C++14constexpr
限制?这是不允许的:
constexpr int i = b[2];
b[2]
不是常量表达式,因为它包含(参考:N4140[expr.const]/2.8)
左值到右值的转换(4.1)或修改(5.17、5.2.6、5.3.2),适用于指工会或其子对象的非活动成员的glvalue
联合的活动成员是结构,因为您初始化了该成员。int数组处于非活动状态
如果将运算符[]
函数更改为开关
,并返回结构的一个成员,它应该可以工作
注意:访问非活动成员会导致未定义的行为。尽管通用编译器支持联合别名作为扩展,但如果您可以将代码设计为不使用联合别名,则可以避免一些麻烦
匿名结构及其初始值设定项也存在问题。具体而言,[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错误。除了M.M的答案之外,根据规则,聚合初始化器始终仅初始化第一个联合成员,该成员将成为该联合的活动成员
因此,将A
更改为int vals[3]
联合中的第一个声明:
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];
};
};
解决了在constepr
表达式中读取匿名union
成员int-vals[3]
的问题。它肯定与union相关。gcc 5.3.1在“A{x,x,x}”初始值设定项上发出嘎嘎声——错误:无法将“x”从“int”转换为“A::”有趣,Clang没有抱怨初始化。A是否违反了聚合初始化规则?好吧,在修补之后,如果我给这个联合一个正式的字段名:“union{…}u;”,然后声明一个显式构造函数:“A(int aa,int bb,int cc):u{aa,bb,cc}{}”,gcc 5.3.1接受该部分,但随后在constexpr操作符[]s上出现了一个重大故障。错误:constexpr非静态成员函数'int&B::operator[](size_t)'的封闭类不是文本类型。我自己也不确定这整件事。我的大脑不能完全围绕一个constexpr操作符[],这就是原因。@SamVarshavchik不知道为什么,但它看起来像是GCC坚持一个({x,x,x}),而不是一个{x,x,x}或一个{x,x,x},所以我更新了我的答案。然而,是的,它对最初的问题没有任何作用。好吧,我对代码进行了修改,gcc 5.3.1吞并了整个问题。因此,gcc 5.3.1将接受适当的殴打和折磨。编辑:就声明而言,gcc仍然存在main()的问题。匿名联合中的匿名结构非常常见,所有主要编译器都支持匿名结构,所以这不是问题。我甚至没有注意到引用的标准指出匿名联合中不允许使用未命名的结构。它涉及嵌套类型,但是异常结构不是类型声明。@plasmacel我认为它算作嵌套类型。CLAN 3.8给出了一个警告,它是一个扩展。CLANG可能警告您,因为它是编译器扩展,因为严格地说,正如我所知道的,匿名结构不是C++标准的一部分。然而,它们是C11标准的一部分,这就是为什么所有主要的C/C++编译器也支持C++11或更早版本A
应该初始化为A{{{x,x,x}}}
,因为我们不只是初始化A
,因为它没有用户定义的构造函数。我们在聚合联合内初始化聚合struct
“联合的活动成员就是结构,因为您初始化了该成员。int数组是非活动的。”通用的初始序列规则难道没有使这一点成为现实吗?
struct A
{
A(int a, int b, int c)
: vals{ a, b c }
{}
union
{
struct { int a, b, c; };
int vals[3];
};
};