通过引用调用`constexpr`成员函数-clang vs gcc

Vit*_*meo 8 c++ language-lawyer constexpr c++17 c++20

考虑以下示例(代码段 (0)):

struct X
{
    constexpr int get() const { return 0; }
};

void foo(const X& x)
{
    constexpr int i = x.get();
}

int main()
{
    foo(X{});
}
Run Code Online (Sandbox Code Playgroud)

与所有版本上面的例子编译g++之前g++ 10.x,从来没有下编译clang++。错误信息是:

error: 'x' is not a constant expression
    8 |     constexpr int i = x.get();
      |
Run Code Online (Sandbox Code Playgroud)

Godbolt.org 上的现场示例

错误是有道理的,因为x在 的主体中从来不是一个常量表达式foo,但是:

  • X::get()被标记constexpr并且它不依赖于的状态x;

  • 更改const X&为const X使用每个编译器(在 Godbolt.org 上) 片段 (1)编译代码。


当我标记X::get()为static( (on Godbolt.org) 片段 (2) )时,它变得更加有趣。有了这个变化,所有测试版本g++(包括主干)都可以编译,但clang++仍然总是无法编译。

所以,我的问题:

  • g++ 9.x接受片段 (0)是否正确?

  • 所有编译器都正确接受代码片段 (1)吗?如果是这样,为什么参考是重要的?

  • 是g++ 9.x和g++ trunk在接受正确的片段(2) ?

wal*_*nut 12

g++ 9.x 在接受代码片段 (0) 时是否正确?

不。

所有编译器都正确接受代码片段 (1) 吗?如果是这样,为什么参考是重要的?

对,他们是。

常量表达式不能使用 id 表达式命名引用,该引用没有先前的常量表达式初始化或在常量表达式评估期间开始其生命周期。[expr.const]/2.11(在 C++20 中相同)

如果您在不涉及任何左值到右值转换的情况下命名非引用变量,则情况并非如此。x.get()only 指的x是 lvalue 并且只调用一个constexpr实际上不访问任何成员的函数x,所以没有问题。

g++ 9.x 和 g++ trunk 在接受代码片段 (2) 时是否正确?

不,因为表达式仍然包含x违反上述规则的子表达式。