プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 25

その他インテル® DPC++/C++ コンパイラー特集

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


25. ‘this’ を nullptr と比較しない

CoreCLR プロジェクトから抜粋した以下のコードについて考えてみます。この危険なコードは、次の PVS-Studio 診断によって検出されます。

V704 ‘this == nullptr’ expression should be avoided – this expression is always false on newer compilers, because ‘this’ pointer can never be NULL. (V704 ‘this == nullptr’ 式は避けるべきです。新しいコンパイラーでは ‘this’ ポインターが null になることはないため、この式は常に false になります。)

bool FieldSeqNode::IsFirstElemFieldSeq()
{
  if (this == nullptr)
    return false;
  return m_fieldHnd == FieldSeqStore::FirstElemPseudoField;
}

説明

以前は、this ポインターを 0/null/nullptr と比較していました。C++ が開発された当初は、これが一般的でした。このようなコードは、「考古学的」な操作を行っていることが分かりました。詳しくは、こちらの記事 (英語) をお読みください。さらに、当時は this ポインターの値を変更することができました。しかし、ずっと前のことなので、皆さんもすでに忘れていることでしょう。

thisnullptr の比較に戻りましょう。

現在、この操作は許可されていません。最近の C++ 標準では、thisnullptr と等しくなることはありません。

C++ 標準によると、null ポインター this に対する IsFirstElemFieldSeq() メソッドの呼び出しは、未定義の動作になります。

this==0 の場合、メソッドを実行している間、このクラスのフィールドにはアクセスできないように見えます。しかし実際には、このようなコードを実装する好ましくない方法が 2 つあります。C++ 標準によると、this ポインターが null になることはないため、コンパイラーは次のようにメソッド呼び出しを最適化できます。

bool FieldSeqNode::IsFirstElemFieldSeq()
{
  return m_fieldHnd == FieldSeqStore::FirstElemPseudoField;
}

もう 1 つ落とし穴があります。以下の継承階層があると仮定します。

class X: public Y, public FieldSeqNode { .... };
....
X * nullX = NULL;
X->IsFirstElemFieldSeq();

Y クラスのサイズが 8 バイトの場合、ソースポインター NULL (0x00000000) は、FieldSeqNode サブオブジェクトの先頭を指すように変更されます。そのため、sizeof(Y) バイトにオフセットする必要があります。つまり、IsFirstElemFieldSeq() 関数で this は 0x00000008 となり、”this == 0″ チェックは全く無意味です。

正しいコード

このケースでは、正しいコード例を提供するのが非常に困難です。関数からこの条件を削除するだけでは十分ではありません。null ポインターを使用して関数を呼び出さないようにコードをリファクタリングする必要があります。

推奨事項

“if (this == nullptr)” は許可されなくなりました。しかし、まだ多くのアプリケーションやライブラリー (例えば、MFC ライブラリー) でこのコードを目にします。そのため、Visual C++* では引き続き this と 0 の比較に対応しています。コンパイラー開発者は、長年適切に動作してきたコードを変更することに積極的ではないのかもしれません。

しかし、最近の C++ 標準では許可されていないので、まずは this と null の比較を避けることから始めます。そして、時間のあるときに、既存のコードを確認して、すべての不正な比較を洗い出し、書き直すと良いでしょう。

おそらく、コンパイラーは今後次のように対応していくと考えられます。最初に、比較に関する警告を出力するでしょう。確認していないので分かりませんが、すでにこのような警告を出しているかもしれません。そしてある時点で、新しい標準を完全にサポートし、既存のコードは動作しなくなるでしょう。そのため、新しい標準に従うことを強く推奨します。そうすることで、後で助かるでしょう。

補足: リファクタリングする場合、null オブジェクト・パターン (英語) が必要になるかもしれません。

トピックの関連情報へのリンク:

  1. まだ “this” ポインターを null と比較していますか? (英語)
  2. 診断 V704 (英語)

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました