c++ - 为什么增强的 GCC 6 优化器会破坏实用的 C++ 代码?

标签 c++ gcc compiler-optimization undefined-behavior

GCC 6 has a new optimizer feature : 它假定 this 始终不为空,并以此为基础进行优化。

Value range propagation now assumes that the this pointer of C++ member functions is non-null. This eliminates common null pointer checks but also breaks some non-conforming code-bases (such as Qt-5, Chromium, KDevelop). As a temporary work-around -fno-delete-null-pointer-checks can be used. Wrong code can be identified by using -fsanitize=undefined.

更改文档明确指出这是危险的,因为它破坏了数量惊人的常用代码。

为什么这个新假设会破坏实际的 C++ 代码? 粗心或不知情的程序员是否会依赖这种特定的未定义行为?我无法想象有人会写 if (this == NULL),因为那太不自然了。

最佳答案

我想需要回答的问题是为什么好心的人会首先写支票。

最常见的情况可能是您的类是自然发生的递归调用的一部分。

如果你有:

struct Node
{
    Node* left;
    Node* right;
};

在 C 中,你可以这样写:

void traverse_in_order(Node* n) {
    if(!n) return;
    traverse_in_order(n->left);
    process(n);
    traverse_in_order(n->right);
}

在 C++ 中,将其设为成员函数非常好:

void Node::traverse_in_order() {
    // <--- What check should be put here?
    left->traverse_in_order();
    process();
    right->traverse_in_order();
}

在 C++ 的早期(标准化之前),人们强调成员函数是函数的语法糖,其中 this 参数是隐式的。代码用 C++ 编写,转换为等效的 C 并编译。甚至有明确的例子表明将 this 与 null 进行比较是有意义的,并且最初的 Cfront 编译器也利用了这一点。因此,来自 C 背景,检查的明显选择是:

if(this == nullptr) return;      

注意:Bjarne Stroustrup 甚至提到 this 的规则多年来已经发生了变化here

这在许多编译器上工作了很多年。当标准化发生时,情况发生了变化。最近,编译器开始利用调用成员函数的优势,其中 thisnullptr 是未定义的行为,这意味着此条件始终为 false ,编译器可以随意省略它。

这意味着要对这棵树进行任何遍历,您需要:

  • 在调用 traverse_in_order

    之前做所有的检查
    void Node::traverse_in_order() {
        if(left) left->traverse_in_order();
        process();
        if(right) right->traverse_in_order();
    }
    

    这意味着还要检查每个调用站点是否可以有一个空根。

  • 不要使用成员函数

    这意味着您正在编写旧的 C 样式代码(可能作为静态方法),并使用对象作为参数显式调用它。例如。你又回到了在调用站点编写 Node::traverse_in_order(node); 而不是 node->traverse_in_order(); 的地方。

  • 我相信以符合标准的方式修复此特定示例的最简单/最简洁的方法是实际使用哨兵节点而不是 nullptr

    // static class, or global variable
    Node sentinel;
    
    void Node::traverse_in_order() {
        if(this == &sentinel) return;
        ...
    }
    

前两个选项似乎都不吸引人,虽然代码可以摆脱它,但他们使用 this == nullptr 编写了糟糕的代码,而不是使用适当的修复程序。

我猜这就是其中一些代码库如何演变为在其中包含 this == nullptr 的检查。

关于c++ - 为什么增强的 GCC 6 优化器会破坏实用的 C++ 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36893251/

相关文章:

c++ - 转换逻辑的目标是什么类型?

C++ 查找从属名称

python - 在 mac 上 easy_install lxml 不工作

c - 改变 gcc 优化开关时意外的文件大小

c++ - 在 Visual Studio2010 中通过 C++ 优化(由编译器完成)的无限循环示例

c++ - 查找一组数据中的最小数字

c++ - C++0x 是否在新特性和标准化过程的重压下崩溃了?

c++ - 如何检查给定调用站点的重载解决方案集

c++ - 我是否误解了这个默认参数 shared_ptr 的范围?

c - 当接收到 null-but-not-0 和 0-but-not-null 指针时,如何测试代码的行为?