1 #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_
2 #define GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_
5 bool_comparable : safe_boolイディオム提供
8 template<typename Derived>
13 operator unspecified_bool_type() const;
14 // 派生したクラスの側で operator! が定義されていない場合、
15 // 派生したクラスの boolean_test() メンバ関数があればその否定を返す。
16 // operator!() も boolean_test() もなければ、エラー
17 bool operator!() const;
19 // bool との比較。 int のときは反応しないようになってます
20 friend bool operator==( const bool_comparable& lhs, bool rhs );
21 friend bool operator==( bool lhs, const bool_comparable& rhs );
22 friend bool operator!=( const bool_comparable& lhs, bool rhs );
23 friend bool operator!=( bool lhs, const bool_comparable& rhs );
29 }; // class bool_comparable<Derived>
32 // 他、呼び出されるとコンパイルエラーになる operator== と operator!= が宣言されています
35 boost::operators みたいな感じに、簡単に safe_bool イディオムを実現します。
36 使用法は、bool_comparable のテンプレート引数に製作するクラスの名前を入れて public 継承。
38 bool operator!() const;
40 bool boolean_test() const;
41 関数のどちらかを定義すればOKです(両方ある場合は operator!() が優先されます)。
44 // 例として boost::optional 的な何かを作りましょう
45 #include <boost/scoped_ptr.hpp>
49 : public gintenlib::bool_comparable< my_optional<T> > // このように使う
56 typedef const T& const_reference;
58 my_optional() : p() {}
59 my_optional( const T& x ) : p( new T(x) ) {}
61 reference operator*() { return *p; }
62 const_reference operator*() const { return *p; }
64 // operator!() を確実に実装することを忘れなければ。
65 bool operator!() const
69 // これにより、bool比較も出来るようになる
71 // ちなみに operator!() じゃなくて
72 // bool boolean_test() const { return p.get(); }
73 // これでもいい(その場合、operator! は自動定義)
75 // そのほか、コピーコンストラクタとか代入演算子とかは面倒だから省略
78 // 面倒なポインタ管理とかしたくないので scoped_ptr を使う
79 boost::scoped_ptr<T> p;
85 my_optional<int> a, b(1), c(0);
90 assert( !"a is empty." );
97 assert( a == 0 && b != 0 && c != 0 );
98 // assert( b == 1 ); // 1 とは比較できない
101 assert( a == false && b == true && c == true );
103 assert( static_cast<bool>(b) == static_cast<bool>(c) );
104 // assert( a != b ); // コレはダメ(もし b と c を比較したら?)
107 ・boost::enable_if を使っています。古いコンパイラでは動きません。
108 ・それが嫌なら GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF マクロを定義すればOKです。
109 ・ただし、その場合、上の例での「b == 1」的な表現がコンパイル通ってしまいます。
111 ・「 a == b 」のような、派生先のクラス同士での比較は、デフォルトではコンパイルエラーになります。
112 ・ですが、当然、派生先で operator== を定義しておけば、きちんと比較できるようになります。
113 ・ただし、派生先のクラスが「 0 から暗黙変換可能」であり「operator==が定義されている」場合は、
114 「 x == 0 」のような比較が、ゼロとの比較に限りですが、出来なくなってしまいます:
115 struct convertible_from_int
116 : gintenlib::bool_comparable<convertible_from_int>,
117 private boost::equality_comparable<convertible_from_int>
120 convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できると・・・
122 bool operator!() const { return value == 0; }
124 // operator== による比較が出来る場合
125 friend bool operator==( const convertible_from_int& lhs, const convertible_from_int& rhs )
127 return lhs.value == rhs.value;
134 convertible_from_int x, y = 2;
135 assert( !x ); // こういうのは普通に出来るが
137 assert( x == false );
140 // assert( x == 0 ); // コレが実行できない(曖昧になってしまう)
141 // assert( y != 0 ); // 当然これもダメ
143 assert( y == 2 ); // でもコレはOK
144 assert( x != 1 ); // コレもOK
147 ・その場合は int との比較を別個定義してやればOKです:
148 struct convertible_from_int
149 : gintenlib::bool_comparable<convertible_from_int>,
150 private boost::equality_comparable<convertible_from_int>,
151 private boost::equality_comparable<convertible_from_int, int>
154 convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できる場合でも
156 bool boolean_test() const { return value; } // boolean_test を使ってみる。
158 friend bool operator==( const piyo& lhs, const piyo& rhs )
160 return lhs.value == rhs.value;
162 // int との比較を明示的に定義さえすれば
163 friend bool operator==( const convertible_from_int& lhs, int rhs )
165 return lhs.value == rhs;
172 convertible_from_int x, y(2)
173 assert( x == 0 ); // OK
179 #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
180 #include "enable_if.hpp"
181 #include <boost/type_traits/is_same.hpp>
184 #include "cast.hpp" // for gintenlib::cast
189 struct bool_comparable_helper
191 // operator bool_type() の戻り値として使われる値
192 void bool_value() const {}
195 // コンパイルエラーを起こすための private 関数
196 bool this_type_does_not_support_comparisons_();
201 template<typename Derived>
202 class bool_comparable
204 // safe-bool に使う型(メンバ関数へのポインタ)
205 typedef void (bool_comparable::*bool_type)() const;
208 const Derived& derived() const { return *static_cast<const Derived*>(this); }
209 Derived& derived() { return *static_cast< Derived*>(this); }
211 // 他のテンプレートでも derived は使いたいぜ
212 template<typename U> friend class bool_comparable;
216 operator bool_type() const
218 return bool_test_() ? bool_value_() : 0;
219 // bool_test_, bool_value_ は後で定義する
224 #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
226 // enable_if で bool と比較する時だけ有効にすることで、x == 1 のような比較を無力化する
227 // todo: g++ 以外のコンパイラでの動作チェック
231 friend typename enable_if<boost::is_same<U, bool>, bool>::type
232 operator==( const Derived& lhs, U rhs )
234 return cast<bool>(lhs) == rhs;
237 friend typename enable_if<boost::is_same<U, bool>, bool>::type
238 operator==( U lhs, const Derived& rhs )
240 return lhs == cast<bool>(rhs);
243 friend typename enable_if<boost::is_same<U, bool>, bool>::type
244 operator!=( const Derived& lhs, U rhs )
246 return cast<bool>(lhs) != rhs;
249 friend typename enable_if<boost::is_same<U, bool>, bool>::type
250 operator!=( U lhs, const Derived& rhs )
252 return lhs != cast<bool>(rhs);
255 #else // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
257 // 一応 enable_if を使わない版も用意
258 // この場合、 x == 1 のような表現が通ってしまうが仕方ないね
259 friend bool operator==( const Derived& lhs, bool rhs )
261 return cast<bool>(lhs) == rhs;
263 friend bool operator==( bool lhs, const Derived& rhs )
265 return lhs == cast<bool>(rhs);
267 friend bool operator!=( const Derived& lhs, bool rhs )
269 return cast<bool>(lhs) != rhs;
271 friend bool operator!=( bool lhs, const Derived& rhs )
273 return lhs != cast<bool>(rhs);
276 #endif // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
281 static bool_type bool_value_()
283 // 関数を無駄に作らせない工夫。どうせ bool_type の値は使わないし
284 return reinterpret_cast<bool_type>( &bool_comparable_helper::bool_value );
288 // まず Derived::operator!() を見に行く
289 bool bool_test_() const
291 return !( !derived() );
295 // Derived 側で operator!() が定義されて無いなら
296 // Derived::boolean_test() 関数がないか探し、無ければコンパイルエラー
297 bool operator!() const
299 return !( derived().boolean_test() );
301 // friend 関数版。存在する意味は特に無い。
302 friend bool boolean_test( const bool_comparable& x )
304 return x.bool_test_();
307 // 比較はエラーになる( Derived 側で特別に定義すればOK)
310 friend bool operator==( const bool_comparable& lhs, const bool_comparable& rhs )
312 return bool_comparable_helper().this_type_does_not_support_comparisons_();
315 friend bool operator!=( const bool_comparable& lhs, const bool_comparable& rhs )
317 return bool_comparable_helper().this_type_does_not_support_comparisons_();
323 ~bool_comparable() {}
325 }; // class bool_comparable<Derived>
327 } // namespace gintenlib
329 #endif // #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_