Constexpr 函数返回联合成员:g++ 与 clang++:无诊断与错误

Constexpr function returning member of union: g++ vs. clang++: no diagnostics vs. error

考虑这段代码:

typedef union { float v; unsigned u; } T;
constexpr T x = { .u = 0 };
constexpr float f(void)
{
        return x.v;
}

此代码有效吗?

调用:

$ g++ t506a.cpp -c -std=c++20 -pedantic -Wall -Wextra
<nothing>

$ clang++ t506a.cpp -c -std=c++20 -pedantic -Wall -Wextra
t506a.cpp:3:17: error: constexpr function never produces a constant expression
      [-Winvalid-constexpr]
constexpr float f(void)
                ^
t506a.cpp:5:9: note: read of member 'v' of union with active member 'u' is not allowed in a
      constant expression
        return x.v;
               ^
1 error generated.

哪个编译器是正确的?

两个编译器都是正确的,即使代码是 ill-formed,因为您显示的程序不需要诊断。确实 f 永远不能被评估为核心常量表达式,但在那种情况下 dcl.constexpr#6 适用:

For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, an evaluated subexpression of the initialization full-expression of some constant-initialized object ([basic.start.static]), the program is ill-formed, no diagnostic required.

(强调我的)

由于不需要诊断,允许GCC不诊断。

另一方面,如果您尝试将 f 计算为常数,例如

constexpr float a = f();

那么就违反了expr.const#5.10:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;

事实上,both GCC and Clang diagnose this error,根据不断评估的要求。