【问题标题】:Is constexpr the new inline?constexpr 是新的内联吗?
【发布时间】:2021-12-19 21:56:24
【问题描述】:

据我所知,c++ 中的 inline 关键字可以追溯到旧的编译器(当时称为“优化编译器”)不能像现代编译器那样优化,因此将函数标记为 @ 987654322@ 告诉编译器这应该被内联,并且作为副作用防止了 ODR 问题。随着编译器变得更好,有人意识到编译器可以比程序员做得更好,因此编译器的inline 要求更像是大多数(全部?)现代编译器忽略的“提示”。

进入 C++11 及后续版本。 constexpr 在我看来似乎处于类似的情况,至少对于它的某些用途,特别是函数和变量。据我了解,它告诉编译器某个函数可能在编译时被评估。但这是编译器应该能够自行解决的问题。一旦编译器在优化方面做得更好,此功能是否也会成为“提示”?

注意:我不是在询问constexpr 的其他用途,例如if 语句。我知道这些是必需的。

【问题讨论】:

  • 虽然constexpr 包含一些与inline 重叠的上下文,但constexpr 远不止这些,与优化无关。
  • @SamVarshavchik 但是你是否认为编译器不能在编译时评估所有可能的东西——即使是没有明确标记为 constexpr 的东西,从而使标记东西 constexpr 只是一个提示?或者编译器没有办法知道是否应该将某些东西标记为 constexpr 吗?我想我要问的是:即使没有明确标记,让一切成为可能 constexpr 是否有缺点。
  • 不可能“让一切皆有可能 constexpr”,因为并非一切都符合成为 constexpr 表达式的技术要求。
  • 不是提示,而是请求。它告诉某个函数必须在编译时进行评估。如果编译器无法弄清楚如何去做(函数不符合条件的数量),则会引发错误。
  • @SamVarshavchik 这就是我所说的“一切可能”的意思,我想我可以改写为“一切都满足 constexpr 要求” 问题是一样的:为什么编译器不只是在编译时评估对所有可能的时间都进行计时,而不必通过标记 constexpr 来告诉它?

标签: c++


【解决方案1】:

但这是编译器应该能够弄清楚的 自己的。一旦编译器获得此功能,此功能是否也会成为“提示” 更擅长优化?

constexpr 不仅仅是一种优化——没有 它,编译器不允许在需要常量表达式的上下文中使用函数,例如在非类型模板参数中。

但我相信你已经知道很多了。真正的问题是:未来的 C++ 标准是否应该允许在常量表达式上下文中使用函数,即使它没有明确标记为 constexpr - 如果它满足 contexpr 的要求?

不,我认为这是与 C++ 开发相反的方向。考虑 C++20 concept。它的主要目标之一是改进错误消息:编译器不是通过嵌套模板定义,而是尽早知道模板参数不满足要求。关键字constexpr 服务于相同的目标:编译器不会遍历函数调用树并发现无法在编译时评估调用堆栈深处的函数,而是提前报告错误。

【讨论】:

    【解决方案2】:

    据我了解,它告诉编译器可能在编译时评估某个函数。

    不是“可能”,而是“可以”。 constexpr 关键字不会告诉编译器它可以做什么(它可能在编译时评估它想要的任何东西)。相反,关键字告诉编译器所需的变量或函数质量,特别是它可以在constant expressions 中使用。如果程序未能满足该要求,编译器将抱怨(错误或警告)。您会收到比其他情况下更相关的错误消息 - 编译器可以告诉您为什么您的实体不符合编译时评估的条件,因为它知道您的意图是让实体成为编译时常量。

    例如,如果您定义了const unsigned a,如果在编译时不知道a 的值,则使用std::array<int, a> 是错误的。错误可能在a 的初始化中,也可能是模板参数应该是b 而不是a。编译器必须将错误报告为“a 不是常量表达式”并让程序员进行调查。另一方面,如果a 被声明为constexpr,编译器会抱怨a 的值在编译时未知的原因,从而减少调试时间。

    如果没有constexpr,以下代码会产生一个可能很弱的错误消息。

    {
        const unsigned a = foo();
        const unsigned b = 42;
    
        std::array<int, a> stuff;  // Error: 'a' is not a constant expression.
        // ...
    }
    

    afoo() 都声明为constexpr 后,错误消失。为什么?因为上周当您编写foo() 时,编译器被告知该函数必须在常量表达式中可用。结果,编译器指出了为什么无法在编译时评估foo(),并且您立即修复了该错误。那是上周,而 foo() 的实现仍然在您的脑海中浮现。这周不是,在做了十几件事之后,包括与编译器争论的时间,因为你认为a 必须是一个常量表达式,因为它是用foo() 初始化的。

    【讨论】:

      【解决方案3】:

      一个理想的编译器可能会找出哪些函数实际上是constexpr,从这个意义上说,人们可以将该关键字视为对编译器的提示。

      但我认为比较 constconstexpr 告诉编译器和人类读者的内容更有意义。一个理想的编译器也可以计算出哪些变量和成员函数应该是const。您可能知道,还有其他充分的理由来标记所有可能的内容const(编译器会为您发现错误,更容易阅读,有助于编译器进行优化)。

      constexpr 也是如此。如果您声明了一个无法在编译时计算的变量constexpr,则会出现错误,您已经记录了该变量可以在编译时计算,它有助于编译器进行优化。

      另请注意,忽略 constexpr 对运行时性能没有意义,而 inline 则不然。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-14
        • 2020-03-16
        • 1970-01-01
        • 1970-01-01
        • 2019-08-27
        相关资源
        最近更新 更多