unspecified_bool_type手法

11446 ワード

C++では、if(obj)やif(!obj)などの構文をサポートするカスタムタイプが必要になる場合があります.つまり、
MyClass obj;
if(obj)
{
//do something
}
if(!obj)
{
//do something
}
これは、ネイティブC++ポインタとの使用法の一貫性を保証できるため、スマートポインタの実装において特に顕著である必要がある.明らかな解決策はoperator bool()変換をリロードすることであるが,このような問題が多すぎて,Effective C++で議論されている.もう一つの方法はoperatorをリロードすることです!、しかし、if(!!obj)のような醜い文法でif(obj)を表現せざるを得ない.
Boostでは、いわゆるunspecified_が広く使われていますbool_type手法、例えばshared_ptrの中:
typedef T * this_type::*unspecified_bool_type;
operator unspecified_bool_type() const//never throws
{
return px == 0? 0: &this_type::px;
}
すると、
shared_ptr sp;
if(sp)
{}
if(!sp)
{}
成功するでしょう
int i = sp;
失敗します.前述の問題を解決した.
しかし、私たちのような怠け者にとって、毎回いくつかの字を少なく書くのはいつも良いですが、この手法はこのようによく使われています.そのため、私たちはそれを抽出してsafeになりたいと思っています.boolベースクラス:
class safe_bool
{
protected:
void safe_bool_true() const {}
typedef safe_bool this_type;
typedef void (this_type::*safe_bool_type)() const;
};
今、使うときはただ
class MyClass : public safe_bool
{
public:
operator safe_bool_type() const//never throws
{ return is_false() ? 0 : &this_type::safe_bool_true; }
//...
};
ここでis_falseはMyClassで定義されており、その具体的な書き方や意味はMyClassの著者が決めることができます.もちろんこのやり方はあまりにも徹底的ではありません.operator safeを書くのは望んでいません.bool_typeのタスクは継承者に残されているので、次のような実行可能な(しかし愚かな)方法があります.
class safe_bool
{
private:
void safe_bool_true() const {}
typedef safe_bool this_type;
typedef void (this_type::*safe_bool_type)() const;
protected:
virtual bool operator!()const = 0;
public:
operator safe_bool_type() const//never throws
{ return operator!() ? 0 : &this_type::safe_bool_true; }
};
クラスを継承してoperatorを再ロードする必要があります!、例:
class MyClass : public safe_bool
{
int data;
public:
bool operator!() const
{ return data == 0; }
};
しかし、これは決して優雅な方法ではありません.すべての継承者にoperatorを再ロードするように要求しているからです.結局少し理不尽で、safe_を継承するため、虚関数を導入しました.boolのクラス自体が小さい(例えばスマートポインタ)ことが多く、もともと虚関数がない可能性が高いので、このような目的のために虚関数ポインタを導入するのはあまりにもお得ではありません.
(まだ何とかしていますが、もうやった人もいるかもしれませんので、交流を歓迎します)
---------------------------------------------------------------------------------------------------
この文章を見て、このテーマについて詳しく話して、みんなと共有しました.
The Safe Bool Idiomby Bjorn Karlsson
ついでにプログラムも貼って忘れておきます
  class safe_bool_base {
protected:
typedef void (safe_bool_base::*bool_type)() const;
void this_type_does_not_support_comparisons() const {}

safe_bool_base() {}
safe_bool_base(const safe_bool_base&) {}
safe_bool_base& operator=(const safe_bool_base&) {return *this;}
~safe_bool_base() {}
};

template <typename T=void> class safe_bool : public safe_bool_base {
public:
operator bool_type() const {
return (static_cast<const T*>(this))->boolean_test()
? &safe_bool_base::this_type_does_not_support_comparisons : 0;
}
protected:
~safe_bool() {}
};

template<> class safe_bool<void> : public safe_bool_base {
public:
operator bool_type() const {
return boolean_test()==true ?
&safe_bool_base::this_type_does_not_support_comparisons : 0;
}
protected:
virtual bool boolean_test() const=0;
virtual ~safe_bool() {}
};

template <typename T, typename U>
void operator==(const safe_bool<T>& lhs,const safe_bool<U>& rhs) {
lhs.this_type_does_not_support_comparisons();
return false;
}

template <typename T,typename U>
void operator!=(const safe_bool<T>& lhs,const safe_bool<U>& rhs) {
lhs.this_type_does_not_support_comparisons();
return false;
}

Here's how to use safe_bool :
  class Testable_with_virtual : public safe_bool<> {
protected:
bool boolean_test() const {
// Perform Boolean logic here
}
};

class Testable_without_virtual :
public safe_bool <Testable_without_virtual> {
public:
bool boolean_test() const {
// Perform Boolean logic here
}
};