constexpr引用匿名结构的变量

时间:2016-04-04 02:08:19

标签: c++ c++11 c++14 unions 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];
    }
};

我声明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限制?

2 个答案:

答案 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的问题。