質問
私は多かれ少なかれこのようなプログラムを書いています:
#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 ++には現在高次関数がありますか?
解決
(もともと一連のコメントでしたが、OPが実際にやりたいことを発見した後、答えとして書き直されました。)
STLコンテナが保管していることに気付きます コピー あなたが挿入するもののよね?それはのインスタンスを意味します MyClass
同等の方が良い(たとえば、via operator==
) - アドレスは常に異なるため、単にアドレスを比較することはできません。
MyClassのコピーを持っている場合、あなたはおそらくより良いです ポインターコンテナ.
そうは言っても、C ++言語はデフォルトでコピーセマンティクスを使用します。言語では、コードで参照のようなものを明示的にする必要があります。私はあなたが拾うことを強くお勧めします 良いC ++本 または、将来このような問題によってつまずかれます。
他のヒント
過去15年間でC ++で物事は劇的に変化しました。 1994年7月、アレクサンダーステファノフの一般的なプログラミングのアイデアを組み込んだ図書館の提案は、ANSI/ISO委員会から最終承認を受けました。このライブラリは、STLがその後標準的なC ++ライブラリになったことに便利に呼び出します。 STLの物語は、その背後にあるアイデアと同じくらい魅力的であり、間違いなく読む価値があります。
std::remove_if()
あなたが見つけた機能は、C ++の現代的アイデンティティの一部となったこの哲学のもう一つの反映です。要するに、これは、要素の任意の容器(シーケンス)および任意の(aのように機能する)条件で機能する一般的な関数です。この効果については、関数を2つのことを提供する必要があります。
- 数個の イテレーター それはあなたが取り組みたい要素の範囲を区別します。
- そしてa 述語 それは、要素に呼び出されたときに、要素を削除し、それ以外の場合はfalseする場合にtrueを返します。
結局のところ、この場合、あなたが望む述語は平等の述語です。また、平等に基づいて要素を削除することは非常に一般的なタスクであるため、標準は 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);
最後に、特定のコンテナの削除関数を呼び出すことにより、本当に要素を削除します。これは、リスト内の単一の位置または削除する連続した要素の範囲で動作します。
things.erase(first_removed, things.end());
この種のコードを単一行で見ることは珍しくありません。
things.erase(std::remove(things.begin(), things.end(), *this),
things.end());
これはすべて圧倒的で複雑に思えるかもしれませんが、いくつかの利点があります。一つには、この標準ライブラリの設計は動的プログラミングをサポートしています。また、標準的なライブラリは、非常にスリムなインターフェイスを備えたコンテナと、さまざまな種類の容器で機能する無料の機能を少なくすることができます。コンテナをすばやく作成し、標準ライブラリのすべての機能を即座に獲得することができます。または、すべての標準コンテナ(すでに書かれているものとまだ書かれていないもの)で即座に動作する一般的な関数をすばやく書き込むことができます。
まず、単純な手書きループ:
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
返されると削除されると見なされます。
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; }