【问题标题】:Invoking `constexpr` member function through reference - clang vs gcc通过引用调用`constexpr`成员函数-clang vs gcc
【发布时间】:2020-06-12 17:50:04
【问题描述】:

考虑以下示例(sn-p (0)):

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

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

int main()
{
    foo(X{});
}

上面的示例使用g++ 10.x 之前的所有g++ 版本编译,而从未在clang++ 下编译。错误信息是:

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

live example on godbolt.org

这种错误是有道理的,因为xfoo 的主体中从来都不是常量表达式,但是:

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

  • const X& 更改为const X 会使代码与每个编译器(on godbolt.org) 一起编译sn-p (1)


当我将X::get() 标记为static ((on godbolt.org) sn-p (2)) 时,它变得更加有趣。通过该更改,g++(包括主干)的所有测试版本都可以编译,而clang++ 仍然始终无法编译。

所以,我的问题:

  • g++ 9.x 接受 sn-p (0) 是否正确?

  • 是否所有编译器都正确接受 sn-p (1)?如果是,为什么参考意义重大?

  • g++ 9.xg++ trunk 是否正确接受 sn-p (2)

【问题讨论】:

标签: c++ c++17 language-lawyer constexpr c++20


【解决方案1】:

g++ 9.x 接受 sn-p (0) 是否正确?

没有。

在接受 sn-p (1) 方面是否所有编译器都是正确的?如果是,为什么参考意义重大?

是的,他们是。

常量表达式不能使用 id 表达式来命名没有先前常量表达式初始化或在常量表达式求值期间开始其生命周期的引用。 [expr.const]/2.11 (same in C++20)

如果您在不涉及任何左值到右值转换的情况下命名非引用变量,则情况并非如此。 x.get() 仅将x 称为左值,并且仅调用constexpr 函数,该函数实际上并未访问x 的任何成员,因此没有问题。

g++ 9.x 和 g++ trunk 在接受 sn-p (2) 方面是否正确?

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

【讨论】:

猜你喜欢
  • 2015-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
  • 2021-11-02
相关资源
最近更新 更多