对匿名结构变量的 constexpr 引用
constexpr reference to a variable of an anonymous struct
给定 struct B
,它从 struct A
:
继承匿名 union
数据成员
#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[]
将 return 值赋给 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
限制?
这是不允许的:
constexpr int i = b[2];
b[2]
不是常量表达式,因为它包含 (ref: N4140 [expr.const]/2.8)
an lvalue-to-rvalue conversion (4.1) or modification (5.17, 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;
联合的活动成员是结构,因为您初始化了该成员。 int 数组处于非活动状态。
如果您将 operator[]
函数更改为 switch
并将 return 改为结构的成员,它应该可以工作。
注意:访问非活动成员会导致未定义的行为。虽然常见的编译器支持联合别名作为扩展,但如果您可以将代码设计为不使用联合别名,就可以避免一些麻烦。
匿名结构及其初始化程序也存在问题。具体来说,[class.union]/5:
A union of the form union { member-specification } ;
is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of an anonymous union shall only define non-static data members. [Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note ]
所以你不能在匿名联合中有匿名结构。您需要将其中之一设为非匿名。例如:
struct A
{
struct AU { int a, b, c; };
union
{
AU a;
int vals[3];
};
};
与初始化程序一起工作 : A({x, x, x})
。您看到的围绕 A
初始化程序的不一致行为可能是 gcc 错误。
除了 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];
};
};
解决了在 constexpr
表达式中读取匿名 union
成员 int vals[3]
的问题。
给定 struct B
,它从 struct A
:
union
数据成员
#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[]
将 return 值赋给 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
限制?
这是不允许的:
constexpr int i = b[2];
b[2]
不是常量表达式,因为它包含 (ref: N4140 [expr.const]/2.8)
an lvalue-to-rvalue conversion (4.1) or modification (5.17, 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;
联合的活动成员是结构,因为您初始化了该成员。 int 数组处于非活动状态。
如果您将 operator[]
函数更改为 switch
并将 return 改为结构的成员,它应该可以工作。
注意:访问非活动成员会导致未定义的行为。虽然常见的编译器支持联合别名作为扩展,但如果您可以将代码设计为不使用联合别名,就可以避免一些麻烦。
匿名结构及其初始化程序也存在问题。具体来说,[class.union]/5:
A union of the form
union { member-specification } ;
is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of an anonymous union shall only define non-static data members. [Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note ]
所以你不能在匿名联合中有匿名结构。您需要将其中之一设为非匿名。例如:
struct A
{
struct AU { int a, b, c; };
union
{
AU a;
int vals[3];
};
};
与初始化程序一起工作 : A({x, x, x})
。您看到的围绕 A
初始化程序的不一致行为可能是 gcc 错误。
除了 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];
};
};
解决了在 constexpr
表达式中读取匿名 union
成员 int vals[3]
的问题。