8

値が設定されているかどうかをチェックする関数の最新技術は何ですか?

たとえば、以下の反復子はセルを解析します。一部のセルには値が含まれており、他のセルは空です。

最も便利な方法は何ですか?

struct iterator 
{                                  //usage:
  bool isset() const               // if (it.isset()) 
  bool isSet() const               // if (it.isSet()) 
  bool empty() const               // if (it.empty()) 

  bool is_set()   const            // if (it.is_set()) 
  bool is_valid() const            // if (it.is_valid()) 

  operator void*() const;          // if (it) 

  explicit operator bool() const;  // if ((bool)it) or if(it) //thanks @stijn
  operator          bool() const;  // if (it) //why not implicit conversion?

  bool operator!() const;          // if (!!it)

  //throwing exception as pointed out by @MatthieuM
  Type get() { if (isSet()) return value_; else throw; }
  //usage:
  //     try {                    // if (it.isSet()) {
  //        Type x = it.get();    //    Type x = it.get();
  //     }                        // }
  //     catch (...) {            // else {
  //        //empty               //    //empty
  //     }                        // }

  //please feel free to propose something different
  ...
};

反射:

  1. 私の上司は理解していないisset()=> に改名isSet()
  2. empty()単一のセルだけでなく、コンテナー コレクションに関するものです :(
  3. operator void*論理的な方法のようですが、C++11 ストリームでは非推奨です
  4. explicit operatorまだサポートされていません(私のコードは古いコンパイラに準拠している必要があります)

私は読んでいます:

4

4 に答える 4

6

void*場合によっては意図されていない有効な変換シーケンスであるため、問題があります。多くの人が C++03 で使用する "セーフ ブール イディオム"と呼ばれることもあります。ここでは、プライベート型を含むローカル メンバー関数ポインター型を使用しているため、クラス外でそのインスタンスを持つことはできません。ただし、それを返して、少なくとも真/偽を確認することはできます。

C++11 を使用している場合explicit operator boolは、ほとんどの場合、まさにこれらのケースのために考案されたものであるため、この方法を使用することをお勧めします。

于 2012-11-02T10:53:48.160 に答える
2

explicit_cast<T>From Imperfect C++: Practical Solutions [...]が言及されていないことに感銘を受けました。概念は非常に単純です。実際には、必要な変換を実装するテンプレート化されたクラスである疑似キーワードを設定します。私はこれを自分の C++ バックポート ライブラリで使用してきましたが、重要な問題はありません。

class MyClass {
  ...implementation
  operator explicit_cast<bool> () const { 
      (return a bool somehow)
  }
};

期待どおりに機能するように疑似キーワードを使用します。

MyClass value;
...
if ( explicit_cast<bool>(myobject) )  {
  do something
} else {
  do something else
}
...

explicit operator何よりも、このソリューションはC++11のネイティブに完全にマッピングできるため、実質的にオーバーヘッドがゼロになり、ネイティブ構文になります。その結果、「isset」、「is_set」、「is_Set」、「isSet」などを呼び出すかどうかを判断しようとするよりも一般的です...

于 2013-11-20T15:49:49.050 に答える
0

すべてのコメント/回答/貢献に感謝します。ここでは、ネット上で見つかったさまざまなアイデアやその他のアイデアをまとめました (多くのドキュメントとソース コードを読みました)。


値の存在を確認する

1. @ j_random_hackerbool isSet()が指摘したように

最も論理的な方法。ソース コード (ライブラリとアプリケーション) はどちらも初心者向けです。そして、これは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
                       |   }

3. @ Matthieu Mが指摘した例外のスロー

一部の人々は、例外はバイナリ コードの最適化に悪いと考えています。ただし、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_lockshared_lockupgrade_lockupgrade_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);

5. bool@ CashCowが提示する安全なイディオム

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を実装していないコンパイラでサイズのペナルティが発生する可能性があると述べています。最近のほとんどのコンパイラは単一継承に関してはそうしていますが、多重継承ではサイズが不利になる場合があります。

于 2012-11-05T01:56:41.510 に答える