54

您可能知道,C++11 引入了constexpr关键字。

C++11 引入了关键字 constexpr,它允许用户保证函数或对象构造函数是编译时常量。[...] 这使编译器能够理解并验证 [函数名称] 是编译时常量。

我的问题是为什么对可以声明的函数的形式有如此严格的限制。我理解希望保证函数是纯的,但考虑一下:

在函数上使用 constexpr 对该函数的功能施加了一些限制。首先,函数必须具有非 void 返回类型。其次,函数体不能声明变量或定义新类型。第三,主体可能只包含声明、空语句和单个返回语句。必须存在参数值,以便在参数替换之后,return 语句中的表达式产生一个常量表达式。

这意味着这个纯函数是非法的:

constexpr int maybeInCppC1Y(int a, int b)
{
    if (a>0)
        return a+b;
    else
        return a-b;
  //can be written as   return  (a>0) ? (a+b):(a-b); but that isnt the point
}

你也不能定义局部变量...... :(所以我想知道这是一个设计决定,还是编译器在证明函数 a 是纯函数时会很糟糕?

4

5 回答 5

29

您需要编写语句而不是表达式的原因是您希望利用语句的附加功能,尤其是循环功能。但要有用,这将需要声明变量的能力(也被禁止)。

如果您将循环工具、可变变量和逻辑分支(如在if语句中)结合起来,那么您就有能力创建无限循环。无法确定这样的循环是否会终止(停止问题)。因此,某些来源会导致编译器挂起。

通过使用递归纯函数,可能会导致无限递归,这可以证明与上述循环功能同等强大。然而,C++ 在编译时已经存在这个问题——它发生在模板扩展中——因此编译器已经必须有一个“模板堆栈深度”的开关,以便他们知道何时放弃。

因此,这些限制似乎旨在确保这个问题(确定 C++ 编译是否会完成)不会变得比现在更棘手。

于 2011-11-29T10:20:45.920 回答
29

函数的规则constexpr被设计成不可能编写constexpr具有任何副作用的函数。

通过要求constexpr没有副作用,用户无法确定实际评估的地点/时间。这很重要,因为constexpr编译器可以自行决定函数在编译时和运行时发生。

如果允许副作用,则需要对观察它们的顺序制定一些规则。这将是非常难以定义的——甚至比static初始化顺序问题更难。

保证这些函数没有副作用的一组相对简单的规则是要求它们只是一个表达式(除此之外还有一些额外的限制)。这听起来最初是有限制的,并排除了您提到的 if 语句。虽然这种特殊情况不会有副作用,但它会给规则带来额外的复杂性,并且考虑到您可以使用三元运算符或递归方式编写相同的东西,这并不是什么大问题。

n2235constexpr是在 C++中提出添加的论文。它讨论了设计的合理性——相关的引用似乎是关于析构函数的讨论中的引用,但通常相关:

原因是常量表达式旨在由编译器在翻译时评估,就像任何其他内置类型的文字一样。特别是不允许有明显的副作用。

有趣的是,该论文还提到,之前的提议建议编译器自动计算出哪些函数constexpr没有 new 关键字,但发现这非常复杂,这似乎支持了我的建议,即规则设计得简单。

(我怀疑论文中引用的参考文献中还会有其他引用,但这涵盖了我关于没有副作用的论点的关键点)

于 2011-11-29T15:26:02.800 回答
13

实际上,C++ 标准化委员会正在考虑为 c++14 移除其中的一些限制。请参阅以下工作文档http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3597.html

于 2013-07-17T21:28:19.317 回答
3

如果不启用在编译期间无法执行的代码,或者不能证明总是停止的代码,这些限制当然可以被取消很多。但是我想它没有完成,因为

  • 它会使编译器复杂化以获得最小的收益。C++ 编译器非常复杂

  • 在不违反上述限制的情况下准确指定允许的数量会很耗时,并且鉴于为了使标准推出而推迟了所需的功能,可能没有动力增加更多的工作(以及进一步延迟标准)为了一点点收益

  • 一些限制要么相当随意,要么相当复杂(尤其是在循环上,因为 C++ 没有本机递增 for 循环的概念,但结束条件和递增代码都必须在for 语句,可以为它们使用任意表达式)

当然,我的假设是否正确,只有标准委员会的成员才能给出权威的回答。

于 2011-11-29T19:24:52.757 回答
-4

我认为 constexpr 仅适用于 const 对象。我是说; 您现在可以拥有静态 const 对象,例如String::empty_string静态构造(无需破解!)。这可能会减少调用“main”之前的时间。静态 const 对象可能具有类似的功能.length(), operator==,...,这就是需要 'expr' 的原因。在“C”中,您可以创建如下静态常量结构:

static const Foos foo = { .a = 1, .b = 2, };

Linux 内核有大量这种类型的类。在 c++ 中,您现在可以使用 constexpr 执行此操作。

注意:我不知道,但不应接受以下代码,例如 if 版本:

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }
于 2013-02-01T08:37:02.460 回答