为什么 C++11 constexpr 如此严格?

发布于 2024-12-18 13:15:34 字数 740 浏览 3 评论 0原文

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

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

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

在函数上使用 constexpr 对什么施加了一些限制 该功能可以做到。首先,函数必须有一个非void返回值 类型。二、函数体不能声明变量或定义new 类型。三、主体中只能包含声明、空语句 和一个返回语句。必须存在参数值,例如 在参数替换之后,返回中的表达式 语句生成一个常量表达式。

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

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 是纯函数时很糟糕?

As you probably know, C++11 introduces the constexpr keyword.

C++11 introduced the keyword constexpr, which allows the user to
guarantee that a function or object constructor is a compile-time
constant.
[...]
This allows the compiler to understand, and verify, that [function name] is a
compile-time constant.

My question is why are there such strict restrictions on form of the functions that can be declared. I understand desire to guarantee that function is pure, but consider this:

The use of constexpr on a function imposes some limitations on what
that function can do. First, the function must have a non-void return
type. Second, the function body cannot declare variables or define new
types. Third, the body may only contain declarations, null statements
and a single return statement. There must exist argument values such
that, after argument substitution, the expression in the return
statement produces a constant expression.

That means that this pure function is illegal:

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
}

Also you cant define local variables... :(
So I'm wondering is this a design decision, or do compilers suck when it comes to proving function a is pure?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

吹泡泡o 2024-12-25 13:15:34

constexpr 函数的规则经过精心设计,使得编写具有任何副作用的 constexpr 函数是不可能的。

通过要求 constexpr 没有副作用,用户就不可能确定它实际评估的位置/时间。这很重要,因为 constexpr 函数可以根据编译器的判断在编译时和运行时发生。

如果允许出现副作用,那么就需要制定一些关于观察副作用的规则。这将非常难以定义 - 甚至比静态初始化顺序问题还要难。

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

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

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

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

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

The rules for constexpr functions are designed such that it's impossible to write a constexpr function that has any side-effects.

By requiring constexpr to have no side-effects it becomes impossible for a user to determine where/when it was actually evaluated. This is important since constexpr functions are allowed to happen at both compile time and run time at the discretion of the compiler.

If side-effects were allowed then there would need to be some rules about the order in which they would be observed. That would be incredibly difficult to define - even harder than the static initialisation order problem.

A relatively simple set of rules for guaranteeing these functions to be side-effect free is to require that they be just a single expression (with a few extra restrictions on top of that). This sounds limiting initially and rules out the if statement as you noted. Whilst that particular case would have no side-effects it would have introduced extra complexity into the rules and given that you can write the same things using the ternary operator or recursively it's not really a huge deal.

n2235 is the paper that proposed the constexpr addition in C++. It discusses the rational for the design - the relevant quote seems to be this one from a discussion on destructors, but relevant generally:

The reason is that a constant-expression is intended to be evaluated by the compiler
at translation time just like any other literal of built-in type; in particular no
observable side-effect is permitted.

Interestingly the paper also mentions that a previous proposal suggested the the compiler figured out automatically which functions were constexpr without the new keyword, but this was found to be unworkably complex, which seems to support my suggestion that the rules were designed to be simple.

(I suspect there will be other quotes in the references cited in the paper, but this covers the key point of my argument about the no side-effects)

森罗 2024-12-25 13:15:34

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

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

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

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

The reason you'd need to write statements instead of expressions is that you want to take advantage of the additional capabilities of statements, particularly the ability to loop. But to be useful, that would require the ability to declare variables (also banned).

If you combine a facility for looping, with mutable variables, with logical branching (as in if statements) then you have the ability to create infinite loops. It is not possible to determine if such a loop will ever terminate (the halting problem). Thus some sources would cause the compiler to hang.

By using recursive pure functions it is possible to cause infinite recursion, which can be shown to be equivalently powerful to the looping capabilities described above. However, C++ already has that problem at compile time - it occurs with template expansion - and so compilers already have to have a switch for "template stack depth" so they know when to give up.

So the restrictions seem designed to ensure that this problem (of determining if a C++ compilation will ever finish) doesn't get any thornier than it already is.

oО清风挽发oО 2024-12-25 13:15:34

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

Actually the C++ standardization committee is thinking about removing several of these constraints for c++14. See the following working document http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3597.html

愁杀 2024-12-25 13:15:34

当然,如果不启用无法在编译时执行或无法证明始终停止的代码,这些限制肯定可以大大取消。但我猜想它没有完成,因为

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

  • 在不违反上述限制的情况下准确指定允许的数量会非常耗时,而且考虑到为了让标准推出而所需的功能已被推迟,因此可能几乎没有增加更多工作(以及进一步延迟标准)却收效甚微

  • 一些限制要么相当任意,要么相当复杂(特别是在循环上,因为 C++ 没有这个概念原生增量 for 循环,但结束条件和增量代码都必须在 for 语句中显式指定,从而可以对它们使用任意表达式)

当然,只有标准委员会的成员可以给个权威答案我的假设是否正确。

The restrictions could certainly be lifted quite a bit without enabling code which cannot be executed during compile time, or which cannot be proven to always halt. However I guess it wasn't done because

  • it would complicate the compiler for minimal gain. C++ compilers are quite complex as is

  • specifying exactly how much is allowed without violating the restrictions above would have been time consuming, and given that desired features have been postponed in order to get the standard out of the door, there probably was little incentive to add more work (and further delay of the standard) for little gain

  • some of the restrictions would have been either rather arbitrary or rather complicated (especially on loops, given that C++ doesn't have the concept of a native incrementing for loop, but both the end condition and the increment code have to be explicitly specified in the for statement, making it possible to use arbitrary expressions for them)

Of course, only a member of the standards committee could give an authoritative answer whether my assumptions are correct.

过期以后 2024-12-25 13:15:34

我认为 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); }

I think constexpr is just for const objects. I mean; you can now have static const objects like String::empty_string constructs statically(without hacking!). This may reduce time before 'main' called. And static const objects may have functions like .length(), operator==,... so this is why 'expr' is needed. In 'C' you can create static constant structs like below:

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

Linux kernel has tons of this type classes. In c++ you could do this now with constexpr.

note: I dunno but code below should not be accepted so like if version:

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文