2013年3月21日木曜日

[c++] if文でポインタがNULLかどうかの判定をするコードのc++言語上の解釈について

ポインタが実体を指しているか、それともNULLか、という判定をして処理を分岐することがよくあります。
if (ptr != NULL) {
         // do some operation.
}

よく見るのは上記のようなコードです。 ですが、NULLとの比較で分岐するのでなく、NULLが0であることを期待した以下のようなコードを見かけることがあります。
if (!ptr) {
     // do some operation.
}
この書き方は問題がありますが、ここではその議論は脇に置いておいて、ptr がポインタではなく、オブジェクトの実体だった場合に何が起こるのか?
という疑問に勝手に応えたいと思います。
class SampleClass {
public:
 SampleClass(int _a) :
 a(_a) {
 }
private:
 int a;
};

int main(int argc, char** argv)
{
 SampleClass s0(0);
 if (s0) {
  printf("s0: true\n");
 } else {
  printf("s0: false\n");
 }
 return 0;
}
上記のコードをコンパイルするとエラーになります。
hoge.cpp:13:8: error: could not convert ‘n0’ to ‘bool’
まあそうですよね。 ここからやっと本題に入りますが、上記のif文をエラーでなくSampleClassの内部状態に応じて、分岐させることが可能です。 具体的にどうすればよいか?答えは「キャスト演算子をオーバーロードする」ことです。
class SampleClass {
public:
 SampleClass(int _a) :
 a(_a) {
 }
 operator bool () {
  // false: if a is 0. true: if a is not 0.
  return a != 0;
 }
private:
 int a;
};
SampleClass を上記のように定義して、bool型へのキャスト演算子を定義するとコンパイルができるようになり、メンバ変数の"a"の値に応じて処理が分岐されるようになります。
この例ではbool型へのキャスト演算子を定義しましたが、int型やvoid*型へのキャスト演算子の定義でも所望の動作をさせることができます。これは暗黙的型変換によってintもしくはvoid*がboolに変換された上でif文で評価されるためです。 実際、stlのiosやbasic_iosではboolではなく、void*へのキャスト演算子が定義されているようです。