constexpr引用匿名结构的变量

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 限制吗?

M.M*_*M.M 9

这是不允许的:

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错误.


pla*_*cel 5

除了 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