标记C ++函数constexpr是否曾经很糟糕?


26

鉴于功能非常微不足道,

int transform(int val) {
    return (val + 7) / 8;
}

很明显,将这个函数变成一个constexpr函数很容易,允许我在定义constexpr变量时使用它,如下所示:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

我的假设是,这严格来说是一种改进,因为该函数仍可以在非constexpr上下文中调用,并且现在也可以用于帮助定义编译时常量变量。

我的问题是,在某些情况下这是个坏主意吗?就像通过执行此功能一样,constexpr我是否可以遇到在特定情况下该功能将不再可用或行为异常的情况?


1
我唯一想到的就是编译器错误。递归constexpr函数调用可能会导致编译步骤非常缓慢,甚至导致编译器内存不足崩溃。
Zan Lynx

Answers:


19

仅当该函数是公共接口的一部分并且您要保持API的将来版本与二进制兼容时,这才有意义。在这种情况下,您必须仔细考虑如何开发API,以及在哪里需要扩展点以进行将来的更改。

这使constexpr预选赛成为不可撤销的设计决策。如果不对API进行不兼容的更改,则无法删除该限定符。它还限制了如何实现该功能,例如,您将无法在该功能内进行任何日志记录。并非所有琐碎的功能都会在永恒中保持琐碎。

这意味着你最好使用constexpr的是功能的本质纯函数,这将是在编译时真正有用的(如模板元编程)。仅由于当前实现恰好是可constexprable的,就使函数成为constexpr是不好的。

在不需要编译时评估的情况下,使用内联函数或具有内部链接的函数似乎更合适constexpr。所有这些变体的共同点是,函数体是“公共”的,并且可以在与调用位置相同的编译单元中使用。

如果所讨论的功能不是稳定的公共API的一部分,则问题不大,因为您可以随意更改设计。但是,由于您现在控制了所有呼叫站点,因此不必“以防万一”将函数constexpr标记为标记。您知道是否在constexpr上下文中使用此函数。然后,添加不必要的限制性限定符可能会被视为混淆。


12

将一个函数标记为constexpr也使其成为内联函数§[dcl.constexpr] / 1:

用constexpr说明符声明的函数或静态数据成员隐式是内联函数或变量(7.1.6)。

inline,则意味着您需要在可能使用该功能的每个翻译单元中包含该功能的定义。这基本上意味着constexpr函数必须是:

  1. 仅限在一个翻译单元中使用,或
  2. 在标题中定义。

您想在标头中声明并在源文件中定义的大多数典型函数(使用它的任何其他函数都包括标头,然后指向该源的目标文件的链接)constexpr根本行不通。

从理论上讲,我想您可以将所有内容移到头文件中,并且只有一个包含所有头文件的源文件,但这会极大地损害编译时间,并且对于大多数严肃的项目来说,需要大量内存才能进行编译。

一个constexpr功能在某些方面也受到限制,因此对于某些功能,它可能根本不是一个选择。限制包括:

  1. 虚函数不能constexpr
  2. 其返回类型必须是“文字类型”(例如,没有具有非平凡的ctor或dtor的对象)。
  3. 它的所有参数都必须是文字类型。
  4. 函数主体不能包含try块。
  5. 它不能包含非文字类型的变量定义,也不能包含具有静态或线程存储持续时间的任何内容。

我跳过了一些相当晦涩的事情(例如,它也不能包含a gotoasm声明),但是您明白了-在很多事情上,它是行不通的。

底线:是的,在很多情况下这是一个糟糕的主意。


“它一定不能是虚拟的(直到C ++ 20)”我想知道虚拟函数如何成为constexpr?编译器做什么?
chaosink
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.