1. ホーム
  2. c++

[解決済み] 強化されたGCC 6オプティマイザは、なぜ実用的なC++コードを壊すのですか?

2022-05-05 22:05:50

質問

GCC 6 には新しいオプティマイザ機能があります : を想定しています。 this は常にnullでないことを前提に最適化されます。

値域伝搬は、C++メンバ関数のthisポインタが非Nullであると仮定するようになりました。これにより、以下のような一般的なNullポインタチェックが不要になります。 が、一部の不適合なコードベース (Qt-5, Chromium, KDevelop など) を破壊してしまいます。 . 一時的な回避策として、 -fno-delete-null-pointer-checks を使用することができます。誤ったコードは、-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 は何年も前から変更されています。 こちら

そして、これは長年にわたって多くのコンパイラで動作していました。標準化が進むと、これは変わりました。さらに最近になって、コンパイラは、メンバ関数を呼び出す際に this ビーイング nullptr は未定義の動作であり、つまりこの条件は常に false また、コンパイラはこれを自由に省略することができます。

つまり、このツリーの探索を行うには、どちらかが必要です。

  • を呼び出す前に、すべてのチェックを行います。 traverse_in_order

    void Node::traverse_in_order() {
        if(left) left->traverse_in_order();
        process();
        if(right) right->traverse_in_order();
    }
    
    

    これは、すべての呼び出し先で、NULLルートがあるかどうかもチェックすることを意味します。

  • メンバー関数を使用しない

    これは、古い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;
        ...
    }
    
    

最初の2つの選択肢はどちらもそれほど魅力的とは思えません。コードはそれで済むかもしれませんが、彼らは悪いコードを書くために this == nullptr 適切な修正方法を使わず

そうやって進化してきたコードベースには this == nullptr のチェックを入れています。