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 );
28 }; // class bool_comparable<Derived>
31 // 他、呼び出されるとコンパイルエラーになる operator== と operator!= が宣言されています
34 boost::operators みたいな感じに、簡単に safe_bool イディオムを実現します。
35 使用法は、bool_comparable のテンプレート引数に製作するクラスの名前を入れて public 継承。
37 bool operator!() const;
39 bool boolean_test() const;
40 関数のどちらかを定義すればOKです(両方ある場合は operator!() が優先されます)。
43 // 例として boost::optional 的な何かを作りましょう
44 #include <boost/scoped_ptr.hpp>
48 : public gintenlib::bool_comparable< my_optional<T> > // このように使う
55 typedef const T& const_reference;
57 my_optional() : p() {}
58 my_optional( const T& x ) : p( new T(x) ) {}
60 reference operator*() { return *p; }
61 const_reference operator*() const { return *p; }
63 // operator!() を確実に実装することを忘れなければ。
64 bool operator!() const
68 // これにより、bool比較も出来るようになる
70 // ちなみに operator!() じゃなくて
71 // bool boolean_test() const { return p.get(); }
72 // これでもいい(その場合、operator! は自動定義)
74 // そのほか、コピーコンストラクタとか代入演算子とかは面倒だから省略
77 // 面倒なポインタ管理とかしたくないので scoped_ptr を使う
78 boost::scoped_ptr<T> p;
84 my_optional<int> a, b(1), c(0);
89 assert( !"a is empty." );
96 assert( a == 0 && b != 0 && c != 0 );
97 // assert( b == 1 ); // 1 とは比較できない
100 assert( a == false && b == true && c == true );
102 assert( static_cast<bool>(b) == static_cast<bool>(c) );
103 // assert( a != b ); // コレはダメ(もし b と c を比較したら?)
106 ・boost::enable_if を使っています。古いコンパイラでは動きません。
107 ・それが嫌なら GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF マクロを定義すればOKです。
108 ・ただし、その場合、上の例での「b == 1」的な表現がコンパイル通ってしまいます。
110 ・「 a == b 」のような、派生先のクラス同士での比較は、デフォルトではコンパイルエラーになります。
111 ・ですが、当然、派生先で operator== を定義しておけば、きちんと比較できるようになります。
112 ・ただし、派生先のクラスが「 0 から暗黙変換可能」であり「operator==が定義されている」場合は、
113 「 x == 0 」のような比較が、ゼロとの比較に限りですが、出来なくなってしまいます:
114 struct convertible_from_int
115 : gintenlib::bool_comparable<convertible_from_int>,
116 private boost::equality_comparable<convertible_from_int>
119 convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できると・・・
121 bool operator!() const { return value == 0; }
123 // operator== による比較が出来る場合
124 friend bool operator==( const convertible_from_int& lhs, const convertible_from_int& rhs )
126 return lhs.value == rhs.value;
133 convertible_from_int x, y = 2;
134 assert( !x ); // こういうのは普通に出来るが
136 assert( x == false );
139 // assert( x == 0 ); // コレが実行できない(曖昧になってしまう)
140 // assert( y != 0 ); // 当然これもダメ
142 assert( y == 2 ); // でもコレはOK
143 assert( x != 1 ); // コレもOK
146 ・その場合は int との比較を別個定義してやればOKです:
147 struct convertible_from_int
148 : gintenlib::bool_comparable<convertible_from_int>,
149 private boost::equality_comparable<convertible_from_int>,
150 private boost::equality_comparable<convertible_from_int, int>
153 convertible_from_int( int x = 0 ) : value(x) {} // int から暗黙変換できる場合でも
155 bool boolean_test() const { return value; } // boolean_test を使ってみる。
157 friend bool operator==( const piyo& lhs, const piyo& rhs )
159 return lhs.value == rhs.value;
161 // int との比較を明示的に定義さえすれば
162 friend bool operator==( const convertible_from_int& lhs, int rhs )
164 return lhs.value == rhs;
171 convertible_from_int x, y(2)
172 assert( x == 0 ); // OK
178 #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
179 #include "enable_if.hpp"
180 #include <boost/type_traits/is_same.hpp>
188 struct bool_comparable_helper
190 // operator bool_type() の戻り値として使われる値
191 void bool_value() const {}
194 // コンパイルエラーを起こすための private 関数
195 bool this_type_does_not_support_comparisons_();
200 namespace bool_comparable_ // ADL 回避
203 template<typename Derived>
204 class bool_comparable
206 // safe-bool に使う型(メンバ関数へのポインタ)
207 typedef void (bool_comparable::*bool_type)() const;
210 const Derived& derived() const { return *static_cast<const Derived*>(this); }
213 typedef ::gintenlib::detail_::bool_comparable_helper helper;
217 operator bool_type() const
219 return bool_test_() ? bool_value_() : 0;
220 // bool_test_, bool_value_ は後で定義する
225 #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
227 // enable_if で bool と比較する時だけ有効にすることで、x == 1 のような比較を無力化する
228 // todo: g++ 以外のコンパイラでの動作チェック
232 friend typename enable_if<boost::is_same<U, bool>, bool>::type
233 operator==( const Derived& lhs, U rhs )
235 return bool(lhs) == rhs;
238 friend typename enable_if<boost::is_same<U, bool>, bool>::type
239 operator==( U lhs, const Derived& rhs )
241 return lhs == bool(rhs);
244 friend typename enable_if<boost::is_same<U, bool>, bool>::type
245 operator!=( const Derived& lhs, U rhs )
247 return bool(lhs) != rhs;
250 friend typename enable_if<boost::is_same<U, bool>, bool>::type
251 operator!=( U lhs, const Derived& rhs )
253 return lhs != bool(rhs);
256 #else // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
258 // 一応 enable_if を使わない版も用意
259 // この場合、 x == 1 のような表現が通ってしまうが仕方ないね
260 friend bool operator==( const Derived& lhs, bool rhs )
262 return bool(lhs) == rhs;
264 friend bool operator==( bool lhs, const Derived& rhs )
266 return lhs == bool(rhs);
268 friend bool operator!=( const Derived& lhs, bool rhs )
270 return bool(lhs) != rhs;
272 friend bool operator!=( bool lhs, const Derived& rhs )
274 return lhs != bool(rhs);
277 #endif // #ifndef GINTENLIB_BOOL_COMPARABLE_NO_ENABLE_IF
282 static bool_type bool_value_()
284 // 関数を無駄に作らせない工夫。どうせ bool_type の値は使わないし
285 return reinterpret_cast<bool_type>( &helper::bool_value );
289 // まず Derived::operator!() を見に行く
290 bool bool_test_() const
292 return !( !derived() );
296 // Derived 側で operator!() が定義されて無いなら
297 // Derived::boolean_test() 関数がないか探し、無ければコンパイルエラー
298 bool operator!() const
300 return !( derived().boolean_test() );
302 // friend 関数版。存在する意味は特に無い。
303 friend bool boolean_test( const bool_comparable& x )
305 return x.bool_test_();
308 // 比較はエラーになる( Derived 側で特別に定義すればOK)
311 friend bool operator==( const bool_comparable& lhs, const bool_comparable& rhs )
313 return helper().this_type_does_not_support_comparisons_();
316 friend bool operator!=( const bool_comparable& lhs, const bool_comparable& rhs )
318 return helper().this_type_does_not_support_comparisons_();
324 // ~bool_comparable() {} // trivial にしたい
326 }; // class bool_comparable<Derived>
328 } // namespace bool_comparable_
330 // gintenlib 名前空間に引き入れる
331 using namespace bool_comparable_;
333 } // namespace gintenlib
335 #endif // #ifndef GINTENLIB_INCLUDED_BOOL_COMPARABLE_HPP_