Volviendo a C ++ después de años de C #, me preguntaba cuál sería la forma moderna (léase: C ++ 11) de filtrar una matriz, es decir, cómo podemos lograr algo similar a esta consulta de Linq:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
¿Para filtrar un vector de elementos ( stringspor el bien de esta pregunta)?
Espero sinceramente que los viejos algoritmos de estilo STL (o incluso extensiones como boost::filter_iterator) que requieren la definición de métodos explícitos sean reemplazados por ahora.

filterPropertyestablecido entrue?Respuestas:
Vea el ejemplo de cplusplus.com para
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_ifevalúa la expresión lambda para cada elementofooaquí y si devuelvetruecopia el valor enbar.El
std::back_inserternos permite insertar nuevos elementos al final debar(usarpush_back()) con un iterador sin tener que redimensionarlo primero al tamaño requerido.fuente
std::copy_ifno es más que un bucle forUn enfoque más eficiente, si en realidad no necesita una nueva copia de la lista, es
remove_if, que en realidad elimina los elementos del contenedor original.fuente
remove_ifen particular porque es la forma de usar el filtro en presencia de una mutación, que es más rápido que copiar una lista completamente nueva. Si estuviera haciendo un filtro en C ++, usaría estocopy_if, así que creo que agrega.remove_ifno cambia elsize(). Necesitarás encadenarloerasepara eso .En C ++ 20, use la vista de filtro de la biblioteca de rangos: (requiere
#include <ranges>)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })devuelve perezosamente los elementos pares en
vec.(Consulte [range.adaptor.object] / 4 y [range.filter] )
Esto ya es compatible con GCC 10 ( demostración en vivo ). Para Clang y versiones anteriores de GCC, la biblioteca range-v3 original también se puede usar, con
#include <range/v3/view/filter.hpp>(o#include <range/v3/all.hpp>) y elranges::viewsespacio de nombres en lugar destd::ranges::views( demostración en vivo ).fuente
Creo que Boost.Range también merece una mención. El código resultante es bastante parecido al original:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });El único inconveniente es tener que declarar explícitamente el tipo de parámetro de lambda. Usé decltype (elements) :: value_type porque evita tener que deletrear el tipo exacto y también agrega un grano de genérico. Alternativamente, con las lambdas polimórficas de C ++ 14, el tipo podría especificarse simplemente como auto:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });FilterElements sería un rango, adecuado para el recorrido, pero básicamente es una vista del contenedor original. Si lo que necesita es otro contenedor lleno de copias de los elementos que satisfacen los criterios (de modo que sea independiente de la vida útil del contenedor original), podría verse así:
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));fuente
Mi sugerencia para C ++ equivalente a C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);Defina una función de plantilla a la que pase un predicado lambda para realizar el filtrado. La función de plantilla devuelve el resultado filtrado. p.ej:
template<typename T> vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate) { vector<T> result; copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); return result; }para usar - dando ejemplos triviales:
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); });fuente
Se mejoró el código pjm siguiendo las sugerencias de subrayado-d :
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; }Uso:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });fuente