Safe bool イディオム
というのを、たまたま知りました。聞いたことがなかったので、「なんじゃそりゃ?」とGoogle先生に伺いを立ててみたところ、こういうものらしいです。
つまりは、オブジェクトをboolとして扱う場合に色々できちゃいけないものとかあるから、それをできないようにしようということです。
で、本題はこのイディオムの意味じゃなくて、その中で出てきている
class Testable { bool ok_; typedef void (Testable::*bool_type)() const; void this_type_does_not_support_comparisons() const {} public: explicit Testable(bool b=true):ok_(b) {} operator bool_type() const { return ok_==true ? &Testable::this_type_does_not_support_comparisons : 0; } };
この「operator bool_type() const...」というのがよくわかりませんでした。operator boolやないのか?と。
というわけで実験してみました。
#include <iostream> // operator boolを定義 class Bool1 { public: operator bool() { return true; } }; // 件のoperator void*()()を定義 class Bool2 { typedef void (Bool2::*test)(); public: operator test() { return false; } }; // 両方定義してみる。 class Bool3 { typedef void (Bool3::*test)(); public: operator test() { return false; } operator bool() { return true; } }; int main(int argc, char *argv[]) { Bool1 b1; Bool2 b2; Bool3 b3; if (b1) { std::cout << "b1 is true" << std::endl; } if (!b1) { std::cout << "b1 is false" << std::endl; } if (b2) { std::cout << "b2 is true" << std::endl; } if (!b2) { std::cout << "b2 is false" << std::endl; } if (b3) { std::cout << "b3 is true" << std::endl; } if (!b3) { std::cout << "b3 is false" << std::endl; } return 0; } 結果 b1 is true b2 is false b3 is true
というわけなので、
- operator boolが定義されており、boolが必要な場面では、operator boolが優先される
- operator void(*)()()でも、ちゃんとboolが必要な場面で使える
ということがわかりました。
さて、一番最初に戻りますが、一番最初の例では、operator bool_type()の返り値に、内部関数のアドレスと、0を返しています。
これも試してみました。
#include <iostream> class Bool4 { typedef void (Bool4::*test)(); void test_not() {} public: operator test() { return &Bool4::test_not; } }; class Bool5 { typedef void (Bool5::*test)(); void test_not() {} public: operator test() { return 0; } }; int main(int argc, char *argv[]) { Bool4 b4; Bool5 b5; if (b4) { std::cout << "b4 is true" << std::endl; } if (static_cast<bool>(b4) == true) { std::cout << "b4 is true" << std::endl; } if (!b4) { std::cout << "b4 is false" << std::endl; } if (b5) { std::cout << "b5 is true" << std::endl; } if (static_cast<bool>(b5) == false) { std::cout << "b5 is false" << std::endl; } if (!b5) { std::cout << "b5 is false" << std::endl; } return 0; } 結果: b4 is true b4 is true b5 is false b5 is false
static_cast
とりあえずこの実験から、
- 関数のアドレスを返したらtrue
- 0を返したらfalse
となりました。よく考えなくても、関数にもアドレスがあるし、0とfalseは同一(だっけ?)なので、0が返されたらfalseになりますね。
ちなみに、void(*)()のoperator overloadは、operator boolに変換されるとのことです。(多分違うけどニュアンスは合ってるはず)
まだまだC++は奥深いです。深すぎて底が見えません。