对匿名结构变量的 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] 的问题。