すべてのコメント/回答/貢献に感謝します。ここでは、ネット上で見つかったさまざまなアイデアやその他のアイデアをまとめました (多くのドキュメントとソース コードを読みました)。
値の存在を確認する
最も論理的な方法。ソース コード (ライブラリとアプリケーション) はどちらも初心者向けです。そして、これはKISS の原則に適合します。さらに、これはJavaなどの他のプログラミング言語に移植できます...
Library: | Application:
|
struct iterator |
{ |
bool isSet() const | if (it.isSet())
{ | {
return p; | int v = it.get();
} | //get() may also call isSet()
|
int get() const | //continue processing
{ | }
return *p; | else //value is not set
} | {
| //do something else
int* p; | }
}; |
関数get()
がチェックせず、(アプリケーションの) 開発者が(前に)isSet()
を呼び出すのを忘れた場合、アプリケーション コードがクラッシュする可能性があります (セグメンテーション違反)。isSet()
get()
一方、get()
関数呼び出しの場合はisSet()
、そのためisSet()
処理が 2 回実行されます。isSet()
それにもかかわらず、最近のコンパイラは、このような 2 番目の不要な処理を避ける必要があります。
2. 同僚の 1 人が提案したフラグ値またはデフォルト値を返す
Library: | Application:
|
struct iterator | int i = it.get()
{ | if (i >= 0)
int get() const | {
{ | unsigned short v = i;
if(p) return *p; |
else return -1; | //continue processing
} | }
| else //value is not set
unsigned short* p; | {
}; | //do something else
| }
一部の人々は、例外はバイナリ コードの最適化に悪いと考えています。ただし、throw exception
がインライン化されている場合、最良のコンパイラはバイナリ コードを最適化する可能性があり、
さらに、このソリューションでisSet()
は が 2 回呼び出されるため、バイナリ コードが最適化される可能性があります。ただし、これはコンパイラの最適化能力に依存します。
としょうかん:
struct iterator
{
bool get() const
{
if (isSet()) return *p;
else throw;
}
private:
bool isSet() const { return ....; }
....
};
応用:
int value;
try
{
value = it.get();
}
catch (...)
{
value = 0; // default value
}
4.使用operator explicit_cast<bool> () const
よく書かれたルイスの答えを参照してください。
5.operator
エレガントな書き方if(it)
このソリューションは、 C++11 で導入された明示的な変換演算子を使用して適切に実装できます。
としょうかん:
struct iterator
{
explicit operator bool() const { return ....; }
....
};
応用:
int value;
if (it) //very elegant C++ fashion
{
value = it.get();
}
else
{
value = 0; // default value
}
ただし、まだ 2012 年であり、現在のソース コードは、明示的な変換演算子をサポートしていないコンパイラに準拠している必要があります。これらのコンパイラでは、さまざまな可能性が何年にもわたって実装されてきました。これらすべてを次の章で紹介します。
if(it)
C++11 より前の enable ステートメント
この章のソース コードは、2004 年にBjarne Stroustrupによって書かれた本More C++ idioms 、より具体的には@PlasmaHHによって指摘されたセクションThe Safe Bool Idiomから着想を得ています。
1.暗黙的operator bool
が利用できない場合は、暗黙の変換 operatorexplicit
を使用できます。
としょうかん:
struct iterator
{
operator bool() const { return ....; } //implicit conversion
....
};
応用:
int value;
if (it) //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}
// But these other instructions are also correct :(
int integer = it; //convert it to bool, then convert bool to int
if (-6.7 < it) //.................., then convert bool to double, and compare
it << 1;
2.operator!
これは、for 、、およびboost::thread
の回避策として (v1.51)で使用されるソリューションです。explicit operator bool()
unique_lock
shared_lock
upgrade_lock
upgrade_to_unique_lock
としょうかん:
struct iterator
{
bool operator!() const { return ....; }
....
};
応用:
int value;
if (!!it) // !! looks strange for many developers
{
value = it.get();
}
else
{
value = 0; // default value
}
if (it) //ERROR: could not convert ‘it’ from ‘iterator’ to ‘bool’
{
value = it.get();
}
3.operator void*
これは、STL ストリームで使用されるソリューションです。たとえば、ファイルbits/basic_ios.h ( std::basic_ios
) を参照してください。
としょうかん:
struct iterator
{
operator void*() const { return ....; }
....
};
応用:
int value;
if (it) //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}
// But these other instructions are also correct :(
delete it; //just a warning: deleting 'void*' is undefined
if (it > std::cin) //both are converted to void*
void* r = it;
4. 未定義の入れ子への暗黙の変換class
この解決策は、1996 年 にDon Boxによって提案されました。
としょうかん:
struct iterator
{
private:
class nested; //just a forward declaration (no definition)
int* v_;
public:
operator nested*() const { return v_ ? (nested*)this : 0; }
};
応用:
int value;
if (it) //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}
// But these other instructions are also correct :(
iterator it2;
if (it < it2)
int i = (it == it2);
Bjarne Stroustrupは、欠点のない究極のソリューションを提案しました。以下は簡易版です。
としょうかん:
struct iterator
{
private:
typedef bool (iterator::*bool_type)() const;
bool private_() const {}
int* v_;
public:
operator bool_type() const { return v_ ? &iterator::private_ : 0; }
};
//forbids it1 == it2
template <typename T>
bool operator == (const iterator& it,const T& t) { return it.private_(); }
//forbids it1 != it2
template <typename T>
bool operator != (const iterator& it,const T& t) { return ! (it == t); }
応用:
int value;
if (it) //this works very well!
{
value = it.get();
}
else
{
value = 0; // default value
}
// All other instructions fail to compile
iterator it2;
if (it > it2) ; //ERROR: no match for ‘operator>’ in ‘it > it2’
if (it == it2) ; //ERROR: ‘bool iterator::private_() const’ is private
if (it != it2) ; //same error
6. 再利用可能な安全なbool
イディオム
これはもっと複雑です。ソース コードについてはWikibooksを参照してください。
としょうかん:
struct iterator : safe_bool <iterator> //I do not want virtual functions
{
bool boolean_test() const { return ....; }
....
};
最近の STL とブーストは機能を提供します。いくつかの例:
- GNU STL --> ファイル tr1/functional および exception_ptr.h を参照
- 各ブースト コンポーネントは独自の を使用します
safe_bool
。
- Spirit --> ファイルspirit/include/classic_safe_bool.hppとspirit/home/classic/core/safe_bool.hppを参照
- IOstream --> ファイル iostreams/device/mapped_file.hpp を参照
- パラメータ --> パラメータ/aux_/maybe.hpp
- オプション
- 関数
- 範囲
- ロジック(トライブール)
しかし、マシュー・ウィルソンは著書Imperfect C++で、Empty Base Optimizationsafe_bool
を実装していないコンパイラでサイズのペナルティが発生する可能性があると述べています。最近のほとんどのコンパイラは単一継承に関してはそうしていますが、多重継承ではサイズが不利になる場合があります。