Note

Von ChatGPT generiert am 2025-05-21.

Im selben Dokument, nicht in anderen (implicit target):

  • Link zum Anchor mit Text des Heading-Elements: filterkonzepte-in-c-von-std-copy-if-bis-views-filter-versuch-1

  • Link zum Anchor mit anderem Text: Anderer Text

  • Link zum Anchor mit anderem Text: draft_cpp_filter_versuch2

Im selben und anderen Dokumenten (explizit target) mit (explizit_link)= vor Heading:

TODO:

Link zu Artikel:

Externe Links können mit spitzen Klammern angegeben werden, so dass man die URL nicht wiederholen braucht:

Filterkonzepte in C++: Von std::copy_if bis views::filter - Versuch #1

Geändert: 2025-06-16 09:59:12, Wörter: 619, Lesedauer: 3 min

In der Softwareentwicklung sind Filter unverzichtbare Werkzeuge – sie helfen uns, Daten zu transformieren, zu reduzieren oder zu selektieren. Ob beim Extrahieren bestimmter Elemente aus einer Liste, dem Entfernen unerwünschter Daten oder dem Einlesen strukturierter Datenströme: Filter abstrahieren logische Bedingungen und entkoppeln sie von der zugrunde liegenden Datenstruktur. In modernen C+±Projekten lassen sich Filter elegant, effizient und idiomatisch einsetzen – sei es über die Standardbibliothek, eigene Wrapper oder neuere Sprachfeatures wie Ranges.

Dieser Artikel gibt einen strukturierten Überblick über Filterkonzepte in C++, zeigt verschiedene Implementierungsarten mit praktischen Beispielen und beleuchtet Best Practices für den Einsatz in der täglichen Programmierung.

Was sind Filter im Kontext von C++?

Allgemein gesprochen ist ein Filter ein Mechanismus zur Auswahl (oder Entfernung) von Elementen basierend auf einer Bedingung. In C++ bedeutet das typischerweise: eine Funktion oder ein Funktor, der ein Element konsumiert und einen booleschen Wert zurückgibt, um zu entscheiden, ob dieses Element „durchgelassen“ wird.

Filter lassen sich in C++ in vielen Formen modellieren:

  • als Prädikate (bool operator()(const T&))

  • als Iterator-Adapter, die nur bestimmte Elemente durchreichen

  • als Algorithmen wie std::copy_if, die einen Container durchlaufen und selektiv kopieren

  • als Streams, bei denen etwa nur bestimmte Daten eingelesen oder geschrieben werden

Im nächsten Abschnitt sehen wir uns die gängigsten Filterarten im Detail an.

Filterarten in C++ im Überblick

STL-Filter mit std::copy_if, std::remove_if und Ranges

Die Standardbibliothek bietet eine Reihe an Algorithmen zur Filterung:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> input = {1, 2, 3, 4, 5, 6};
    std::vector<int> output;

    std::copy_if(input.begin(), input.end(), std::back_inserter(output),
                 [](int x) { return x % 2 == 0; }); // nur gerade Zahlen

    for (int n : output)
        std::cout << n << " ";
}

Seit C++20 wird das durch die Ranges-Bibliothek noch eleganter:

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5, 6};

    for (int x : data | std::views::filter([](int v) { return v > 3; }))
        std::cout << x << " ";
}

Eigene Filterfunktionen

Filter lassen sich auch als generische, wiederverwendbare Funktionen modellieren:

template<typename InputRange, typename Predicate>
auto filter(const InputRange& range, Predicate pred) {
    std::vector<typename InputRange::value_type> result;
    for (const auto& item : range) {
        if (pred(item))
            result.push_back(item);
    }
    return result;
}

Diese einfache Utility-Funktion kann bereits viele Filteraufgaben übernehmen und ist mit Lambda-Ausdrücken flexibel einsetzbar.

Iterator-Wrapper und Filter-Adapter

Ein fortgeschrittener Ansatz ist das Schreiben eigener Iteratoren, die beim Traversieren nur bestimmte Elemente weitergeben. Das entspricht z. B. einem “Filter-View”, wie er ab C++20 Teil der Standardbibliothek ist.

Für frühere Standards (C++11 bis C++17) kann man sich eigene Iterator-Adapter bauen. Ein Beispiel mit Boost:

#include <boost/range/adaptors.hpp>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    auto even = [](int x) { return x % 2 == 0; };
    for (int x : vec | boost::adaptors::filtered(even)) {
        std::cout << x << " ";
    }
}

Stream-Filter: selektives Lesen und Schreiben

Filter lassen sich auch direkt beim Einlesen von Daten anwenden, z. B. über eine Wrapper-Klasse für std::istream.

#include <sstream>
#include <iostream>
#include <string>

int main() {
    std::istringstream iss("foo 123 bar 456 baz 789");
    std::string token;

    while (iss >> token) {
        if (std::all_of(token.begin(), token.end(), ::isdigit)) {
            std::cout << "Zahl gefunden: " << token << "\n";
        }
    }
}

Diese Technik kann bei der Verarbeitung von Logdateien, CSV-Importen oder anderen Eingabeströmen nützlich sein.

Best Practices und Stolpersteine

  • Benutze Standardalgorithmen zuerst: In 90 % der Fälle reicht std::copy_if, std::remove_if oder seit C++20 views::filter.

  • Iteratoren nicht invalidieren: Wenn du in-place filterst (z. B. mit erase), beachte die Gültigkeit von Iteratoren.

  • Lambdas bevorzugen: Kurze Filterbedingungen sollten als Lambda-Funktionen formuliert werden – das macht den Code kompakter und verständlicher.

  • Achte auf Performance bei großen Datenmengen: Vermeide unnötige Kopien. Verwende Ranges mit Lazy Evaluation oder std::move_iterator, wenn möglich.

  • Lesbarkeit vor Cleverness: Filter sollten klar und intuitiv lesbar sein – nicht durch obskure Metaprogrammierung erschwert werden.

Weiterführende Bibliotheken und Features

  • Boost.Range bietet seit Jahren mächtige Werkzeuge für Filter und Transformationen.

  • C++23 std::views::filter ist der empfohlene moderne Ansatz – performant, lesbar und composable.

  • Ranges-v3 von Eric Niebler ist die inoffizielle Grundlage für C++20 Ranges und bietet mehr Kontrolle und Erweiterungsmöglichkeiten.

Fazit

Filter sind ein zentrales Konzept in der modernen C+±Entwicklung. Sie ermöglichen saubere, modulare und idiomatische Datenverarbeitung – egal ob über STL, eigene Funktionen, Iteratoradapter oder Streams. Wer sich mit den verschiedenen Möglichkeiten auseinandersetzt und die richtigen Tools einsetzt, kann lesbaren und performanten Code schreiben, der exakt das tut, was er soll.

Weitere Einblicke in verwandte Themen wie Transformations-Views, ranges::transform, sowie Iterator-Konzepte findest du in meinem kommenden Beitrag über Range-Pipelines und funktionale Komposition in C++.