2012-04-22 10 views
7

次のC++ STLプログラムでは、ファンクタNthを定義し、n回目にrevokeされた場合はtrueを返します。そして、汎用アルゴリズムremove_ifに変換します。奇妙な。ファンクタを述語として使用するC++ STLプログラム

コード:

#include <iostream> 
#include <list> 
#include <algorithm> 
#include "print.hpp" 

using namespace std; 

class Nth{ 
private: 
    int nth,ncount; 
public: 
    Nth(int n):nth(n),ncount(0){} 

    bool operator()(int) 
    { 
     return ++ncount == nth; 
    } 
}; 

int main() 
{ 
    list<int> col; 
    for (int i = 1;i <=9 ;++i) 
    { 
     col.push_back(i); 
    } 

    PRINT_ELEMENTS(col,"col : "); 

    list<int>::iterator pos; 
    pos = remove_if(col.begin(),col.end(), 
     Nth(3)); 

    col.erase(pos,col.end()); 

    PRINT_ELEMENTS(col,"nth removed : "); 
} 

print.hpp:

#include <iostream> 

template <class T> 
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") 
{ 
    typename T::const_iterator pos; 

    std::cout << optcstr; 
    for (pos=coll.begin(); pos!=coll.end(); ++pos) { 
     std::cout << *pos << ' '; 
    } 
    std::cout << std::endl; 
} 

私は、Microsoft Visual Studio 2008でそれを実行し、私は結果を得る:それはその要素3と6を削除 enter image description here を私は望んでいない。私は3つだけ削除されると思った。 誰かが私のために解釈できますか?ありがとうございました。

答えて

11

:により、チュートリアルとリファレンスニコライM. Josuttis氏

これが起こる理由は、アルゴリズム中の述語内部アルゴリズムのコピーの通常の実装:

template <class ForwIter, class Predicate> 
    ForwIter std::remove_if(ForwIter beg, ForwIter end, 
          Predicate op) 
    { 
     beg = find_if(beg, end, op); 
     if (beg == end) { 
      return beg; 
     } 
     else { 
     ForwIter next = beg; 
      return remove_copy_if(++next, end, beg, op); 
     } 
    } 

アルゴリズムが使用しますfind_if()を使用して、削除する必要がある最初の要素を探します。ただし、渡された述語opのコピーを使用して残りの要素があれば処理します。ここで、元の状態のNthが再び使用され、残りの要素の3番目の要素(実際には6番目の要素)も削除されます。

この動作はバグではありません。この標準では、述部がアルゴリズムによって内部的にコピーされる頻度を指定していません。したがって、C++標準ライブラリの保証された動作を得るには、その動作がコピーまたは呼び出される頻度に依存する関数オブジェクトを渡すべきではありません。したがって、2つの引数の単項述語を呼び出して、両方の引数が等しい場合、述部は常に同じ結果を返さなければなりません。つまり、述語は呼び出しのために状態を変更するべきではなく、述語のコピーは元のものと同じ状態を持つ必要があります。関数呼び出しのために述語の状態を変更できないようにするには、operator()を定数メンバ関数として宣言する必要があります。

+1

より正確には、OPが達成したいことはまだ可能です。状態は外部化され、変更可能な参照の形で述部に渡されます。与えられた述語のすべてのコピーは元のものと同じ変更可能な状態を共有します。 –

5

std::liststd::remove_ifを使用しないでください。代わりに、リストのメンバ関数を使用する:あなたが安全に終了から消去することができるように

col.remove_if(Nth(3)); 

一般的なアルゴリズムは、要素の値を並べ替えますが、リストのために、メンバーのアルゴリズムは触れず、不要なノードを直接削除しますその他の要素。

更新。あなたの述語は値渡しの内部状態を持つことが許可されていないので、これはあなたの問題を解決するために実際には保証されていません。これを代わりに試してください:

struct Nth 
{ 
    const int n; 
    int & counter; 
    Nth(int N, int & c) : n(N), counter(c) { } 
    bool operator()(int) const { return ++counter == N; } 
}; 

{ 
    int counter = 0; 
    cols.remove_if(Nth(3, counter)); 
} 

この新しい述語はコピー可能であり、(外部)カウンタ変数の周りの参照ラッパーとして機能します。 C++標準ライブラリの

+0

これは、述語の単一のコピーを使用することだけを保証するものではなく、 'std :: remove_if'以外のものを保証するものではありません。この問題を解決するように見える場合は、偶然に過ぎません。 –

+0

@MikeSeymour:はい、そうです。私はメモを追加します。 –

0

私は、「標準C++ライブラリ」を読んで、私は別のsolution.Thatは見つける:再実装関数remove_if:

template <class ForwIter,class Predicate> 
ForwIter remove_if_re(ForwIter begin,ForwIter end,Predicate op) 
{ 
    while(begin != end && !op(*begin)) 
     ++begin; 
    if(begin == end) 
     return begin; 
    else{ 
     ForwIter next = begin; 
     return remove_copy_if(++next,end,begin,op); 
    } 
} 

それは作業を行います。

しかし、私はちょっと好奇心が強いです。これは、渡された述語opのコピーを使用して残りの要素を処理しませんか?

私はSTLを学ぶのが初めてです。私はあなたの患者の答えを尊敬します。

ありがとうございます。

関連する問題