为什么C ++ 11 constexpr如此限制?

时间:2011-11-29 09:55:45

标签: c++ c++11 constexpr

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

  

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

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

  

在一个函数上使用constexpr会对什么造成一些限制   那个功能可以做到。首先,函数必须具有非void返回   类型。其次,函数体不能声明变量或定义新的   类型。第三,正文可能只包含声明,空语句   和一份退货声明。必须存在参数值   在参数替换后,表达式返回   statement生成一个常量表达式。

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

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
}

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

5 个答案:

答案 0 :(得分:28)

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

如果将用于循环的工具与可变变量和逻辑分支(如在if语句中)组合在一起,那么您就能够创建无限循环。无法确定这样的循环是否会终止(the halting problem)。因此,某些源会导致编译器挂起。

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

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

答案 1 :(得分:28)

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

通过要求constexpr没有副作用,用户无法确定实际评估的位置/时间。这很重要,因为允许constexpr函数在编译时和运行时都由编译器自行决定。

如果允许副作用,那么就需要对它们被观察的顺序有一些规则。这将非常难以定义 - 甚至比static初始化订单问题更难。

保证这些函数无副作用的一组相对简单的规则是要求它们只是一个表达式(除此之外还有一些额外的限制)。这听起来很有限,并排除了你所说的if语句。虽然这个特殊情况没有副作用,但它会在规则中引入额外的复杂性,并且假设您可以使用三元运算符编写相同的内容,或者递归地说它并不是真正的大问题。

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

  

原因是常量表达式旨在由编译器进行评估   在翻译时,就像任何其他字面的内置类型一样;特别是没有   允许观察到的副作用。

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

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

答案 2 :(得分:12)

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

答案 3 :(得分:3)

如果没有启用在编译期间无法执行的代码,或者无法证明总是停止的代码,那么这些限制肯定可以提升很多。但是我想这不是因为

  • 它会使编译器复杂化,以获得最小的增益。

  • 就像C ++编译器一样复杂
  • 在不违反上述限制的情况下准确指定允许的数量是非常耗时的,并且考虑到所需的功能已被推迟以便将标准推出门外,可能没有什么动力去添加更多工作(以及标准的进一步延迟)以获得微不足道的收益

  • 一些限制可能相当随意或相当复杂(特别是在循环上,假设C ++没有循环原生递增的概念,但结束条件和增量代码都有要在for语句中明确指定,可以为它们使用任意表达式)

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

答案 4 :(得分:-4)

我认为constexpr只适用于const对象。我的意思是;你现在可以静态地拥有静态const对象,例如String::empty_string构造(没有黑客攻击!)。这可能会缩短调用“main”之前的时间。静态const对象可能有.length(), operator==,...之类的函数,所以这就是为什么需要'expr'的原因。在'C'中,您可以创建如下的静态常量结构:

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

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

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

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }