web-dev-qa-db-ja.com

ベクターから要素を削除するためのstd :: remove_ifのより良い代替手段はありますか?

std::vectorまたは他のコンテナから特定のプロパティを持つ要素を削除するタスクは、機能的なスタイルの実装に役立ちます。

ただし、C++でこれを行う標準的な方法は、次のイディオムのようです。

std::vector<int> ints;
...
ints.erase(
    std::remove_if(ints.begin(), 
                   ints.end(),
                   [](int x){return x < 0;}),
    ints.end());

この例では、整数ベクトルからゼロ未満のすべての要素を削除します。

Itいだけでなく、間違った使い方も簡単だと思います。 std::remove_ifは、ベクトルのサイズを変更できないことは明らかです(名前が示すとおり)。イテレータのみが渡されるためです。しかし、私自身を含む多くの開発者は、最初はそれを理解していません。

それで、これを達成するためのより安全で、できればもっとエレガントな方法がありますか?そうでない場合、なぜですか?

25
Frank Puffer

Itいだけでなく、間違った使い方も簡単だと思います。

心配しないで、私たち全員が最初にやった。

反復子のみが渡されるため、std :: remove_ifがベクトルのサイズを変更できないことは明らかです(名前が示すとおり)。しかし、私自身を含む多くの開発者は、最初はそれを理解していません。

同じ。それは皆を混乱させます。おそらく何年も前にremove_ifと呼ばれるべきではなかったでしょう。後知恵?

それで、これを達成するためのより安全で、できればもっとエレガントな方法がありますか?

番号

そうでない場合、なぜですか?

これは、アイテムを削除するとイテレータが無効になるコンテナからアイテムを削除するときにパフォーマンスを維持する最も安全で最もエレガントな方法であるためです。

予想:

私にできることは何ですか?

はい、このイディオムを関数にラップします

template<class Container, class F>
auto erase_where(Container& c, F&& f)
{
    return c.erase(std::remove_if(c.begin(), 
                                  c.end(),
                                  std::forward<F>(f)),
                   c.end());    
}

動機付けの例の呼び出しは次のようになります。

auto is_negative = [](int x){return x < 0;};
erase_where(ints, is_negative);

または

erase_where(ints, [](int x){return x < 0;});
25
Richard Hodges

これは、C++ 17対応のコンパイラで std::experimental::erase_if アルゴリズムによりまもなく利用可能になります。

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <experimental/vector>

int main()
{
    std::vector<int> ints { -1, 0, 1 };   
    std::experimental::erase_if(ints, [](int x){
        return x < 0;
    });
    std::copy(ints.begin(), ints.end(), std::ostream_iterator<int>(std::cout, ","));
}

Live Example 0,1を出力

14
TemplateRex