使用constexpr表达式检查共同初始序列中未激活的联合成员

3

根据C++标准中的12.3.1点:

如果一个标准布局联合包含多个共享公共初始序列的标准布局结构体,并且如果该标准布局联合类型的对象的非静态数据成员是活动状态且是其中一个标准布局结构,则允许检查任何标准布局结构成员的公共初始序列;

然而,以下代码在任何主要编译器上都不能被编译(https://godbolt.org/z/3jM1co):

struct type {
    union {
        int a;
        int b;
    };

    constexpr type(int n) : a(n) {}
};

constexpr int fun(int n) {
    type t(n);

    return t.b;
}

constexpr int res = fun(5);

为什么代码编译不通过(我相信所有编译器都不会错)?这段代码应该满足访问联合体中的技术非活动成员的条件(标准布局,公共初始序列)。当删除 constexpr 关键字时,这段代码就可以顺利编译。


2
联合体不包含任何标准布局结构,只有简单类型。将ab转换为结构体。移除constexpr后,代码具有未定义行为。 - Richard Critten
1
无所谓。当使用结构体包装“a”和“b”时,会出现相同的错误。 - Bargor
1个回答

1

你引用的文本是正确的,但在constexpr上下文中访问非活动联合体成员存在额外的限制。特别地,你正在违反这个规则

一个表达式E是核心常量表达式,除非按照抽象机器的规则([intro.execution])评估E将评估以下之一:

应用于引用非活动联合体成员或其子对象的glvalue的lvalue-to-rvalue转换;


请注意,您可以在constexpr上下文中更改联合的活动成员,因此您可以执行以下操作:
constexpr int fun(int n) 
{
    type t(n);
    t.b = t.a;    // t.b is now the active member
    return t.b;   // ok, reading from active member is fine
}

我认为这只有在c++20中才被允许:演示

相关的 规则 是这样的:

表达式 E 是一个核心常量表达式,除非按照抽象机器([intro.execution])的规则评估 E 将评估以下之一:

对于其活动成员(如果有),其可变联合体的隐式定义的复制/移动构造函数或复制/移动赋值运算符的调用,除非联合体对象的生存期始于 E 的评估内

(强调是我的)。由于 t 的生命周期始于 fun 的评估内部,因此这是允许的。


我明白原因。但对我来说,这很不幸,并且提出的解决方法可能无法解决我的问题。 - Bargor
@Bargor 我并没有提出修复方案,只是指出了问题。不过,这个修复方案似乎是可行的。你对此有什么疑虑吗?它似乎与原始代码具有相同的语义(而且它可以工作)。 - cigien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接