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

同カテゴリーの次の記事

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

この記事は、インテル® デベロッパー・ゾーンに公開されている「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 (英語)

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

関連記事

  • Parallel Universe マガジンParallel Universe マガジン Parallel Universe へようこそ。 米国インテル社が四半期に一度オンラインで公開しているオンラインマガジンです。インテルの技術者によるテクノロジーの解説や、最新ツールの紹介など、並列化に関する記事を毎号掲載しています。第1号からのバックナンバーを PDF 形式で用意しました、ぜひご覧ください。 12 […]
  • 比較関数の罠比較関数の罠 この記事は、インテル® デベロッパー・ゾーンに公開されている「The Evil within the Comparison Functions」の日本語参考訳です。 この記事の PDF […]
  • 並列プログラミングにおけるロックの効率的な使用並列プログラミングにおけるロックの効率的な使用 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Using Locks Effectively in Parallel Programming […]
  • インテル® AVX 命令を使用した complex float データ型の IIR フィルターの実装インテル® AVX 命令を使用した complex float データ型の IIR フィルターの実装 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「Intel® AVX Realization Of IIR Filter For Complex Float Data」の日本語参考訳です。 はじめに この記事では、インテル® AVX の SIMD (Single Instruction […]
  • インテル® AVX を使用した IIR ガウスぼかしフィルターの実装インテル® AVX を使用した IIR ガウスぼかしフィルターの実装 この記事は、インテル® ソフトウェア・ネットワークに掲載されている「IIR Gaussian Blur Filter Implementation using Intel® Advanced Vector Extensions」の日本語参考訳です。 ソースのダウンロード: gaussian_blur.cpp […]