21

他のトピックでは、この問題を解決しようとしていました。問題は、から重複する文字を削除することでしたstd::string

std::string s= "saaangeetha";

順序は重要ではないため、s最初に並べ替えてから使用std::uniqueし、最終的にサイズを変更して目的の結果を取得しました。

aeghnst

それは正しいです!


今、私は同じことをしたいのですが、同時に文字の順序をそのままにしたいのです。つまり、次の出力が必要です。

sangeth

だから私はこれを書いた:

template<typename T>
struct is_repeated
{
    std::set<T>  unique;
    bool operator()(T c) { return !unique.insert(c).second; }
}; 
int main() {
    std::string s= "saaangeetha";
    s.erase(std::remove_if(s.begin(), s.end(), is_repeated<char>()), s.end()); 
    std::cout << s ;
}

次の出力が得られます。

saangeth

つまり、a繰り返されますが、他の繰り返しはなくなります。コードの何が問題になっていますか?

とにかく、コードを少し変更します:(コメントを参照)

template<typename T>
struct is_repeated
{
    std::set<T> & unique;  //made reference!
    is_repeated(std::set<T> &s) : unique(s) {} //added line!
    bool operator()(T c) { return !unique.insert(c).second; }
}; 
int main() {
    std::string s= "saaangeetha";
    std::set<char> set; //added line!
    s.erase(std::remove_if(s.begin(),s.end(),is_repeated<char>(set)),s.end()); 
    std::cout << s ;
}

出力:

sangeth

問題がなくなりました!

では、最初の解決策の何が問題なのですか?

また、メンバー変数をunique参照型にしないと、問題は解決しません

std::setまたはis_repeatedファンクターの何が問題になっていますか? 問題はどこにありますか?

is_repeatedまた、ファンクターがどこかにコピーされると、そのすべてのメンバーもコピーされることに注意してください。ここに問題はありません!

4

7 に答える 7

17

ファンクターは、ファンクターのコピーが元のファンクターと同一になるように設計されているはずです。つまり、1 つのファンクターのコピーを作成して一連の操作を実行すると、どのファンクターを使用しても、または 2 つのファンクターをインターリーブしても、結果は同じになります。これにより、STL 実装は、ファンクタをコピーして適切に渡す柔軟性を得ることができます。

最初のファンクターでは、この主張は成立しません。なぜなら、ファンクターをコピーして呼び出した場合、格納されたセットに加えた変更が元のファンクターに反映されないため、コピーとオリジナルのパフォーマンスが異なるためです。同様に、2 番目のファンクターを使用してそのセットを参照によって保存しないようにすると、ファンクターの 2 つのコピーは同じようには動作しません。

ただし、ファンクターの最終バージョンが機能する理由は、セットが参照によって格納されるという事実が、tue ファンクターの任意の数のコピーが互いに同じように動作することを意味するためです。

お役に立てれば!

于 2011-03-22T20:53:52.320 に答える
15

GCC (libstdc++)では、remove_if基本的に次のように実装されています。

    template<typename It, typename Pred>
    It remove_if(It first, It last, Pred predicate) {
      first = std::find_if(first, last, predicate);
    //                                  ^^^^^^^^^
      if (first == last)
         return first;
      else {
         It result = first;
         ++ result;
         for (; first != last; ++ first) {
           if (!predicate(*first)) {
    //          ^^^^^^^^^
              *result = std::move(*first);
              ++ result;
           }
         }
      }
    }

述語はに値渡しされるためfind_if、内部で変更された構造体、したがってセットはfind_if呼び出し元に伝播されないことに注意してください。

最初の複製は次の場所に表示されるため:

  saaangeetha
//  ^

イニシャルはお電話"sa"後お預かりいたしますfind_if。一方、predicateのセットは空です (内部の挿入find_ifはローカルです)。したがって、その後のループは 3 番目を保持しaます。

   sa | angeth
// ^^   ^^^^^^
// ||   kept by the loop in remove_if
// ||
// kept by find_if
于 2011-03-22T21:07:41.817 に答える
5

実際には答えではありませんが、考慮すべきもう1つの興味深い情報として、これは元のファンクターを使用していても機能します:

#include <set>
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>

template<typename T>
struct is_repeated {
    std::set<T>  unique;
    bool operator()(T c) { return !unique.insert(c).second; }
}; 
int main() {
    std::string s= "saaangeetha";
    std::remove_copy_if(s.begin(), s.end(), 
                        std::ostream_iterator<char>(std::cout), 
                        is_repeated<char>());
    return 0;
}

編集:この動作に影響を与えるとは思いませんが、ファンクタのマイナーなスリップも修正しました(operator()は明らかにタイプTのパラメータを取る必要があり、ではありませんchar)。

于 2011-03-22T21:07:24.343 に答える
4

問題は、is_repeatedファンクターがの実装内のどこかにコピーされていることにあると思いますstd::remove_if。その場合は、デフォルトのコピーコンストラクターが使用され、これによりstd::setコピーコンストラクターが呼び出されます。最終的には、2つのis_repeatedファンクターが独立して使用される可能性があります。ただし、両方のセットは別個のオブジェクトであるため、相互の変更は認識されません。フィールドを参照に変えるとis_repeated::unique、コピーされたファンクターは、この場合に必要な元のセットを引き続き使用します。

于 2011-03-22T20:51:05.393 に答える
2

Functor クラスは純粋な関数である必要があり、独自の状態を持たない必要があります。これに関する適切な説明については、Scott Meyer の効果的な STLブックの項目 39 を参照してください。しかし、その要点は、ファンクタークラスがアルゴリズム内で1回以上コピーされる可能性があるということです。

于 2011-03-22T21:21:40.363 に答える
1

問題は、使用しているファンクターがコピー可能ではないという点で、他の答えは正しいです。特に、gcc (4.2) に付属する STL は、削除する最初の要素を検索するためのと、操作を完了するためのstd::remove_ifの組み合わせとして実装されます。std::find_ifstd::remove_copy_if

template <typename ForwardIterator, typename Predicate>
std::remove_if( ForwardIterator first, ForwardIterator end, Predicate pred ) {
   first = std::find_if( first, end, pred ); // [1]
   ForwardIterator i = it;
   return first == last? first 
          : std::remove_copy_if( ++i, end, fist, pred ); // [2]
}

[1] のコピーは、見つかった最初の要素がファンクターのコピーに追加されることを意味し、最初の 'a' が忘れ去られて失われることを意味します。[2] ではファンクターもコピーされていますが、そのコピーのオリジナルが空のファンクターであるため、そうでなくても問題ありません。

于 2011-03-22T21:38:22.940 に答える