为什么**不**声明一个函数是`constexpr`?

Lar*_*ars 58 c++ const function-declaration constexpr c++11

任何只包含return语句的函数都可以声明 constexpr,因此如果所有参数都是,constexpr并且只constexpr在其体内调用函数,则允许在编译时进行计算.有没有理由不宣布任何此类功能constexpr

例:

  constexpr int sum(int x, int y) { return x + y; }
  constexpr i = 10;
  static_assert(sum(i, 13) == 23, "sum correct");
Run Code Online (Sandbox Code Playgroud)

任何人都可以提供一个例子来声明一个函数constexpr 会造成什么伤害吗?


一些初步想法:

即使没有充分的理由宣布一个函数,constexpr我也无法想象该constexpr关键字具有过渡角色:它在代码中的缺失不需要编译时评估将允许那些不实现编译时评估的编译器仍然编译该代码(但是在使用需要它们的代码上可靠地失败constexpr).

但是我不明白:如果没有充分的理由不宣布函数constexpr,为什么标准库中的每个函数都没有被声明constexpr?(你不能说它还没有完成,因为还没有足够的时间去做,因为为所有人做这件事是明智的 - 与决定每一个功能是否成功相反constexpr.) - - 我知道N2976 故意不要求cstrs用于许多标准库类型,例如容器,因为这对于可能的实现来说太有限了.让我们从参数中排除它们并且只是想知道:一旦标准库中的一个constexpr类型实际上有一个cstr,为什么不是每个函数都在它上面声明constexpr

在大多数情况下,你也不能说你可能不想constexpr仅仅因为你没有设想任何编译时使用而声明一个函数 :因为如果其他人退出.将使用您的代码,他们可能会看到您没有这样的用途.(当然,也可以用于类型特征类型和类似的东西.)

所以我想有一个很好的理由和一个故意不宣布功能的好例子constexpr

("每个函数"我总是指:满足存在要求的每个函数constexpr,即被定义为单个return语句,只接受带有constexpr cstrs的类型的参数,并且只调用constexpr函数.)

问题为什么std::forward丢弃constexpr- 性? 这是一个特例.

Ant*_*ams 34

只有在constexpr遵守以下规则时才能声明函数:constexpr没有动态强制转换,没有内存分配,没有调用非constexpr函数等.

在标准库中声明一个函数,constexpr要求所有实现都遵守这些规则.

首先,这需要检查可以实现的每个功能constexpr,这是一项漫长的工作.

其次,这是对实现的一个很大的限制,并且将禁止许多调试实现.因此,如果收益超过成本,或者要求足够严格以至于实施几乎必须遵守constexpr规则,那么这是值得的.对每个功能进行评估也是一项长期工作.

  • 制作一些东西`constexpr`是非常有限的.这不是我会轻易做到的事情,因为一旦它成为你的界面的一部分,由于向后兼容性问题很难改变它.即使对于标准库的给定实现,这也适用 - 许多人将无意中依赖于他们使用的库的实现属性,因此我不希望实现使任何函数`constexpr`与标准所要求的不同,或者没有合理的实施会违反限制. (2认同)
  • 首先,`constexpr` 函数是隐式的`inline`,所以如果你不想把你的函数定义放在你的头文件中,它们就不能是`constexpr`。其次,声明一个函数`constexpr`意味着你不能使用try块、`static`变量、`thread_local`变量或非文字类型的变量,所以不能使用`std::string`或`std::vector`等. (2认同)
  • 如果你有一个可以是 `constexpr` 的 `inline` 函数,那么声明它没有任何缺点,除了它可能会限制你将来——将实现更改为不兼容 `constexpr`是 ABI 中断,因为在您的代码中的某个地方可能会在常量表达式中使用它。 (2认同)

tem*_*def 14

我认为你所指的是部分评估.您所接触的是,某些程序可以分为两部分 - 一部分需要运行时信息,一部分可以在没有任何运行时信息的情况下完成 - 理论上您可以完全评估程序的一部分在开始运行程序之前不需要任何运行时信息.有一些编程语言可以做到这一点.例如,D编程语言在编译器中内置了一个解释器,允许您在编译时执行代码,前提是它满足某些限制.

部分评估工作面临一些主要挑战.首先,它使编译器的逻辑变得非常复杂,因为编译器需要能够模拟在编译时可以放入可执行程序的所有操作.在最坏的情况下,这需要你在编译器内部有一个完整的解释器,这会产生一个难题(编写一个好的C++编译器)并使其难以处理.

我认为当前规范的原因constexpr仅仅是限制编译器的复杂性.它受限的情况很容易检查.没有必要在编译器中实现循环(这可能会导致另外一大堆问题,例如,如果在编译器中获得无限循环会发生什么).它还避免了编译器可能必须评估可能在运行时导致段错误的语句,例如跟踪错误的指针.

要记住的另一个考虑因素是某些功能具有副作用,例如从cin网络连接读取或打开网络连接.从根本上讲,这些函数无法在编译时进行优化,因为这样做只需要在运行时可用的知识.

总而言之,没有理论上的理由让你无法在编译时部分评估C++程序.事实上,人们总是这样做.例如,优化编译器本质上是试图尽可能地执行此操作的程序.模板元编程是C++程序员尝试在编译器中执行代码的一个实例,并且可以使用模板做一些很好的事情,因为模板规则构成了一种函数式语言,编译器可以更容易地实现.此外,如果您考虑编译器作者小时和编程时间之间的权衡,模板元编程表明,如果您可以让程序员向后弯腰以获得他们想要的东西,您可以构建一个非常弱的语言(模板系统)并保持语言复杂性简单.(我说"弱",如"不特别表达",而不是"可计算性理论意义上的"弱").

希望这可以帮助!

  • 这是一个非常有用的答案,我同意你的观点,即TMP提供了一种非常好的纯函数式语言.虽然,`constexpr`也允许编写纯函数代码,同时在运行时和编译时变量之间提供了一个很好的桥梁.但是,没有像`std :: find`这样的函数和其他一些定义`constexpr`的函数似乎人为地限制了可以使用STL编写的纯代码的种类. (3认同)