web-dev-qa-db-fra.com

Manière moderne de filtrer le conteneur STL?

Revenant au C++ après des années de C #, je me demandais ce que serait la manière moderne - lire: C++ 11 - de filtrer un tableau, c’est-à-dire comment obtenir quelque chose de similaire à cette requête Linq:

var filteredElements = elements.Where(Elm => Elm.filterProperty == true);

Afin de filtrer un vecteur d'éléments (strings pour les besoins de cette question)?

J'espère sincèrement que les anciens algorithmes de style STL (ou même des extensions comme boost::filter_iterator) nécessitant de définir méthodes explicites sont maintenant remplacés?

59
ATV

Voir l'exemple de cplusplus.com pour std::copy_if :

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_if Évalue l'expression lambda pour chaque élément de foo ici et si elle renvoie true, elle copie la valeur dans bar.

Le std::back_inserter Nous permet d’insérer de nouveaux éléments à la fin de bar (en utilisant Push_back()) avec un itérateur sans avoir à le redimensionner au préalable.

73
Sebastian Hoffmann

Une approche plus efficace, si vous n'avez pas réellement besoin d'une nouvelle copie de la liste, est remove_if , qui supprime réellement les éléments du conteneur d'origine.

32
djhaskin987

Je pense que Boost.Range mérite également une mention. Le code résultant est assez proche de l'original:

#include <boost/range/adaptors.hpp>

// ...

using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& Elm)
    { return Elm.filterProperty == true; });

Le seul inconvénient est d'avoir à déclarer explicitement le type de paramètre de lambda. J'ai utilisé decltype (elements) :: value_type car cela évite d'avoir à épeler le type exact, et ajoute également un grain de généricité. Alternativement, avec les lambdas polymorphes de C++ 14, le type pourrait simplement être spécifié comme étant auto:

auto filteredElements = elements | filtered([](auto const& Elm)
    { return Elm.filterProperty == true; });

filtréElements serait une plage, adaptée à la traversée, mais il s’agit essentiellement d’une vue du conteneur d’origine. Si vous avez besoin d'un autre conteneur rempli de copies des éléments répondant aux critères (afin qu'il soit indépendant de la durée de vie du conteneur d'origine), il pourrait ressembler à ceci:

using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& Elm)
    { return Elm.filterProperty == true; }), back_inserter(filteredElements));
22
user2478832

Ma suggestion pour l'équivalent C++ de C #

var filteredElements = elements.Where(Elm => Elm.filterProperty == true);

Définissez une fonction de modèle à laquelle vous transmettez un prédicat lambda pour effectuer le filtrage. La fonction template renvoie le résultat filtré. par exemple:

template<typename T>
vector<T> select_T(vector<T> inVec, function<bool(const T&)> predicate)
{
  vector<T> result;
  copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
  return result;
}

utiliser - donnant des exemples triviaux:

std::vector<int> mVec = {1,4,7,8,9,0};

// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });

// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });
9
pjm

Amélioré pjm code suivant nderscore-d suggestions:

template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
    Cont result;
    std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
    return result;
}

Usage:

std::vector<int> myVec = {1,4,7,8,9,0};

auto filteredVec = filter(myVec, [](int a) { return a > 5; });
7
AlexP11223

En C++ 20, utilisez la vue filtre de la bibliothèque de plages:

vec | view::filter([](int a){ return a % 2 == 0; })

retourne paresseusement les éléments pairs dans vec.

(Voir [range.adaptor.object]/4 et [range.filter] )

3
L. F.