Explicit operator bool overload実装の経緯と標準ライブラリによる使用例
Explicit operator boolを用いた値の比較において、operator==を用いたif文は、オーバーロードなどを定義しない限り、コンパイルが通らない。
class A{ bool data; public: A(bool arg):data(arg){} explicit operator bool()const noexcept{return data;} }; int main() { A x{true}; if(x); if(x==true); // decltype(x)==A,decltype(true)==bool }
何故ならばこの場合==演算子を用いた記述は、xを直接投込むのとはセマンティックが異なるからである。explicitによって暗黙の変換を防いでいるのだから当然といえば当然の事だ。
そもそもexplit operator boolはどのような経緯で実装されたのか。
経緯
例えば、スマートポインタのようなクラスを実装する上でif文でスマートポインタの有効、無効を確認したい場合operator boolをオーバーロードする。
template<class _Tp> class smart_ptr{ _Tp* pointer_d; public: smart_ptr(_Tp* arg):pointer_d(arg){} operator bool()const{return pointer_d!=0;} ~smart_ptr(){delete pointer_d;} }; void f() { smart_ptr<int> p(new int(0)); if(p){ // ポインタは有効 }else{ // ポインタは無効 } // 無意味なコード int f=p; std::cout<<f<<std::endl; }
しかしboolのオーバーロードをした場合、無意味な代入や出力のコンパイルが通ってしまう。C++03までではunspecified_bool_typeを用いる事でそれを防いでいたが、仕様を変動させるモノがライブラリ頼りなのはあまり適切でないし、視覚的にも分かりにくい。
それを改善するため、C++11でexplicit operator boolが実装された。これをオーバーロードすれば、C++03以前のイディオムを用いる必要は無い。
$ cat test.cpp template<class _Tp> class smart_ptr{ _Tp* pointer_d; public: smart_ptr(_Tp* arg):pointer_d(arg){} explicit operator bool() const{return pointer_d;} ~smart_ptr(){delete pointer_d;} }; void f() { smart_ptr<int> p(new int(0)); if(p); int f=p; } $ g++ test.cpp test.cpp: 関数 ‘int main()’ 内: test.cpp:17:8: エラー: cannot convert ‘smart_ptr<int>’ to ‘int’ in initialization int f=p; ^
通らないif文
しかし、このコードにおいても当然だがoperator==を使ったif文のコンパイルは通らない。
$ cat test.cpp #include<cstddef> template<class _Tp> class smart_ptr{ _Tp* pointer_d; public: smart_ptr(_Tp* arg):pointer_d(arg){} smart_ptr(std::nullptr_t):smart_ptr(){} explicit operator bool() const noexcept{return pointer_d;} ~smart_ptr(){delete pointer_d;} }; void f() { smart_ptr<int> p(new int(0)); if(p==true); } $ g++ test.cpp test.cpp: 関数 ‘int main()’ 内: test.cpp:18:6: エラー: no match for ‘operator==’ (operand types are ‘smart_ptr<int>’ and ‘bool’) if(p==true); ~^~~~~~ test.cpp:18:6: 備考: candidate: operator==(int, int) <組み込み> test.cpp:18:6: 備考: 第 1 引数を ‘smart_ptr<int>’ から ‘int’ へ変換する方法が不明です
こういったスマートポインタではif文の中でnull_ptrと比較をしたい場合があるため、こういったエラーは譲れない。
そこで標準ライブラリーのスマートポインタ(shared_ptr, unique_ptr)は、nullptrを受け取るコンストラクタを用意する事でこの問題を回避している。