How do compilers know C++ constexpr computations do not trigger undefined behavior?(编译器如何知道C++常量计算不会触发未定义的行为?)
问题描述
C++标准要求编译器在C++常量计算中检查未定义的行为。
在this talk中,Chandler Carruth指出,在检查UB时"您将耗尽检测错误的能力",而且在一般情况下,检测UB与halting problem相关,因此可以证明无法确定。
他指的不是conexpr中的UB,但conexpr计算从C++14开始就像常规程序一样通用,因此这仍然适用。
那么,当编译器无法确定程序是否是UB程序时,它们会做些什么?他们还会接受这个程序,祈祷自己继续编译吗?或者,他们更加保守,拒绝接受这项计划,即使它可能是正确的?(我个人的感觉是他们这样做)
对我来说,这很重要,因为我用Clang编译得很好的非平凡指针算法进行了常量计算,但用GCC编译失败了,我非常确定这不是UB。你可以说这是一个GCC的错误,但如果UB是无法决定的,那么所有的编译器在这方面都是错误的。
更根本的是,为什么标准要求无UB?有没有技术上的原因?或者更具哲理性的说法("如果编译器不能检查,程序员就会触发UB,就会产生不好的结果")?
我认为这与C++的其余部分不一致,C++的其余部分永远不会阻止您搬起石头砸自己的脚。我宁愿GCC接受我的常量代码并崩溃,或者如果是UB就发出垃圾;而不是当它不知道自己是不是UB时不编译。
= 编辑=正如M.M和Nicol Bolas所指出的,该标准规定了限制(甚至在C++14中),因此我们永远不会遇到停顿问题类型的UB。然而,我仍然在想,检查UB是否可能太复杂,如果编译器启发式失败,那么他们会(可能不正确地)将其标记为非常量。
但我从评论中感觉到,这更多的是实现不成熟的问题。
推荐答案
在这次演讲中,Chandler Carruth表示,在检查UB时"您将耗尽检测错误的能力",而且在一般情况下,检测UB与停止问题有关,因此可以证明不可能做出决定。
暂停的问题是,当您获取一个程序并尝试确定该程序在执行时是否一定会暂停。根据定义,停止问题仅将程序视为锁定对象。
持续评估是...评估。您正在执行程序,而不仅仅是查看源代码。
当程序的执行执行某些未定义的操作时,会发生未定义的行为。大多数UB案例不能确定是明确定义的,也不能仅仅通过检查源代码来确定。请考虑以下代码:
void foo(void *ptr)
{
*reinterpret_cast<int*>(ptr) = 20;
}
那是不是UB?这要视情况而定;如果将指向int
的指针传递到foo
中,那么它将是定义良好的。此代码是否定义良好只能由执行方式确定。
常量计算需要执行代码;这就是我们通常将其称为编译时执行的原因。当您执行代码时,可以知道foo
的特定执行是否传递了指向实际int
的指针(忽略reinterpret_cast
在constexpr
代码中被禁止的事实)。因此,在评估时,您可以知道是否发生了UB。
那么,当编译器无法确定程序是否是UB程序时,他们该怎么办?
这实际上不是一件可能发生的事情。假设规范是完整的并且没有漏洞,程序的执行是否表现出定义良好的行为仅仅是遵循规范的问题。
您与GCC和Clang之间的问题不是能否确定UB的问题。
更根本的是,为什么标准要求不含UB?是否有技术原因?
假设,我们可以从C++甚至C中删除所有未定义的行为。我们可以让一切都是先验定义的,并从语言中删除其计算结果不能从基本原则中确定的任何内容。
标准不会这样做,因为这会很糟糕。它会阻止我们做各种有用的、低级的事情。它将阻止有用的编译器优化。以此类推。
这些原因都不适用于编译时代码执行。尤其是"有用的、低级的东西"这一部分。对于编译后的代码,生成的代码在一台实际的机器上执行。因此,有一个后门来与真实的机器对话是有意义的。然而,在编译时,没有真正的机器可供对话;只有C++定义的抽象机器。那么允许UB有什么意义呢?
编译器不会生成并执行机器语言;常量求值基本上是在编译器内执行脚本语言。与大多数脚本语言一样,您希望它能够安全而正确地进行计算。您希望快速捕获错误(UB是错误),并在故障点提供干净的错误消息,而不是在过程中随意死亡。这篇关于编译器如何知道C++常量计算不会触发未定义的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:编译器如何知道C++常量计算不会触发未定义的行为?
基础教程推荐
- 您如何将 CreateThread 用于属于类成员的函数? 2021-01-01
- 设计字符串本地化的最佳方法 2022-01-01
- C++ 标准:取消引用 NULL 指针以获取引用? 2021-01-01
- 如何定义双括号/双迭代器运算符,类似于向量的向量? 2022-01-01
- C++ 程序在执行 std::string 分配时总是崩溃 2022-01-01
- 调用std::Package_TASK::Get_Future()时可能出现争用情况 2022-12-17
- C++,'if' 表达式中的变量声明 2021-01-01
- 运算符重载的基本规则和习语是什么? 2022-10-31
- 什么是T&&(双与号)在 C++11 中是什么意思? 2022-11-04
- 如何在 C++ 中处理或避免堆栈溢出 2022-01-01