1

私は多かれ少なかれこのようなプログラムを書いています:

#include <list>

list<MyClass> things;

class MyClass {
   // some stuff

   void remove() {
       things.remove_if(THING_IS_ME);
   }
};

THING_IS_MEの代わりに何を書く必要がありますか?

言い換えれば、私は物事のコレクションとしてグローバルSTLリストを使用しています。ある時点で、リストにあるオブジェクトは、それが冗長であることを認識し、a)リストから自分自身を削除し、b)自分自身を破壊したいと考えています。

どうすればよいですか?

私は約15年間C++を作成しておらず、このページで少し混乱しています:http ://www.cplusplus.com/reference/algorithm/remove_if/

これらの述語は何ですか?C ++には高階関数がありますか?

4

4 に答える 4

5

過去15年間で、C++では状況が劇的に変化しました。1994年7月、ジェネリックプログラミングのアイデアを組み込んだライブラリのAlexander Stepanovの提案は、ANSI/ISO委員会から最終承認を受けました。今日STLと呼ばれるこのライブラリは、その後標準C++ライブラリになりました。STLのストーリーは、その背後にあるアイデアと同じくらい魅力的であり、間違いなく読む価値があります。

std::remove_if()あなたが見つけた機能は、C++の現代的なアイデンティティの一部となったこの哲学の単なる別の反映です。要するに、これは要素の任意のコンテナ(シーケンス)および任意の(aのように機能する)条件で機能するジェネリック関数です。このためには、関数に2つのことを提供する必要があります。

  1. 作業したい要素の範囲を区切る2つのイテレータ。
  2. また、要素で呼び出されたときに、要素が削除される場合はtrueを返し、それ以外の場合はfalseを返す述語。

結局のところ、この場合、必要な述語は平等の述語です。また、等式に基づいて要素を削除することは非常に一般的なタスクであるため、標準はstd::remove()暗黙の等式述語を想定する関数も提供します。もちろん、要素が比較できることを確認する必要があります。

bool operator==(const MyClass& a, const MyClass& b)
{
    // return true if the two are equal, and false otherwise.
}

次に、述語を使用して、次のタイプの要素を削除できますMyClass

std::remove(things.begin(), things.end(), *this);  // if *this == elem

std::remove()標準関数は、まだ作成されていないコンテナも含め、どのコンテナでも機能することを思い出してください。あらゆる種類のコンテナには独自の要素の削除方法があるため、この関数は、動作するコンテナの実装の詳細を知らなければ、実際に削除を実行することはできません。したがって、代わりに、std::remove()関数は、「削除された」要素がコンテナの最後にあるように要素を交換します。次に、「削除された」連続する要素の最初の要素を指すイテレータを返します。

typedef std::list<MyClass>::iterator iter;
iter first_removed = std::remove(things.begin(), things.end(), *this);

最後に、特定のコンテナの削除関数を呼び出して要素を本当に削除します。この関数は、リスト内の1つの位置、または連続する要素の範囲で機能して、次のように削除します。

things.erase(first_removed, things.end());

この種のコードが1行で表示されることは珍しくありません。

things.erase(std::remove(things.begin(), things.end(), *this),
             things.end());

これはすべて圧倒的で複雑に見えるかもしれませんが、いくつかの利点があります。一つには、この標準ライブラリの設計は動的計画法をサポートしています。また、標準ライブラリは、非常にスリムなインターフェイスを備えたコンテナと、さまざまな種類のコンテナで動作するいくつかの無料の関数を提供できます。これにより、コンテナをすばやく作成し、標準ライブラリのすべての機能をすぐに利用できるようになります。または、すべての標準コンテナー(既に作成されているコンテナーとまだ作成されていないコンテナー)で即座に機能するジェネリック関数をすばやく作成できます。

于 2010-10-20T02:50:54.430 に答える
2

(元々はコメントのセットでしたが、OPが実際に何をしたいのかを発見した後、回答として書き直されました。)

STLコンテナには、挿入したもののコピーが格納されていることに気づいていますか?つまり、MyClassより良いインスタンスは(たとえば経由でoperator==)比較可能です-アドレスは常に異なるため、単にアドレスを比較することはできません。

MyClassのコピーが意味をなさない場合は、ポインタコンテナを使用したほうがよいでしょう。

そうは言っても、C++言語はデフォルトでコピーセマンティクスを使用します。この言語では、参照などをコードで明示的にする必要があります。良いC++の本を手に入れることを強くお勧めします。そうしないと、将来このような問題に遭遇するでしょう。

于 2010-10-20T02:00:26.823 に答える
0

まず、簡単な手書きのループ:

for( list<MyClass>::iterator it = things.begin(); it != things.end(); /*  */ ) {
    if( qualifiesForDelete( *it ) ) {
        it = things.erase( it );
    }
    else {
        ++it;
    }
}

次に、remove_ifアルゴリズムを使用します。remove_ifのメンバー関数とは対照的に、アルゴリズムであるlistことは、実際には要素を削除できません。むしろ、削除する要素をリストの最後に移動します。その後erase、呼び出される必要があります。これは非常に重要なイディオムであり、消去-削除イディオムであり、学習する必要があります。

things.erase( 
    remove_if( things.begin(), things.end(), deletionPredicate ), 
    things.end() 
);

ここdeletionPredicateで、は型の単一の引数を取り、を返す関数または関数オブジェクトですbool。返された要素はtrue削除されたと見なされます。

于 2010-10-20T01:37:10.490 に答える
0

このremove_if関数は3つのパラメーターを取ります。作業中の範囲を定義する2つのイテレーターと、述語(を返すテスト関数bool)です。開始イテレータは、作業する最初の要素を指します。終了イテレータは、範囲内の最後の要素の後の要素を指します。

リンクしたページの例では、述語は次のようなコード行です。

bool IsOdd (int i) { return ((i%2)==1); }

コードに必要なことを実行させるには、次のように記述する必要があります。

things.remove_if(things.begin(), things.end(), SomePredicateFunction);

そして、あなたはSomePredicateFunctionそのように定義するでしょう(true適切なテストに置き換えます):

bool SomePredicateFunction (MyClass c) { return true; }
于 2010-10-20T02:09:16.323 に答える