Ab C++11 hat sich die Sprache stark verändert. Diese Kapitel zeigen, was heute zum "modernen C++" gehört.
Ranges (C++20)
Mit <ranges> kombinierst du Bearbeitungsschritte wie in einer Pipeline, lazy und ohne Zwischenkopien:
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector<int> zahlen{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto ergebnis = zahlen
| std::views::filter([](int x) { return x % 2 == 0; }) // nur gerade
| std::views::transform([](int x) { return x * x; }) // quadrieren
| std::views::take(3); // die ersten drei
for (int x : ergebnis) std::cout << x << ' ';
std::cout << '\n';
for (int i : std::views::iota(1, 6)) std::cout << i << ' '; // 1 bis 5
std::cout << '\n';
for (int x : zahlen | std::views::reverse | std::views::drop(7)) std::cout << x << ' ';
std::cout << '\n';
std::ranges::sort(zahlen, std::greater<>{}); // Algorithmus direkt auf dem Container
std::cout << zahlen.front() << ' ' << std::ranges::count(zahlen, 5) << '\n';
return 0;
}4 16 36 1 2 3 4 5 3 2 1 10 1
Structured Bindings und Tupel
#include <iostream>
#include <tuple>
#include <map>
#include <string>
std::tuple<int, double, std::string> daten() { return {1, 2.5, "text"}; }
int main() {
auto [i, d, s] = daten();
std::cout << i << ' ' << d << ' ' << s << '\n';
std::map<std::string, int> m{{"a", 1}, {"b", 2}};
for (const auto& [k, v] : m) std::cout << k << v << ' ';
std::cout << '\n';
std::pair<std::string, int> p{"Mia", 17};
auto& [name, alter] = p;
alter++;
std::cout << p.second << '\n';
return 0;
}1 2.5 text a1 b2 18
Konstante Ausdrücke: constexpr und consteval
Funktionen und Objekte, die der Compiler schon beim Übersetzen auswertet:
#include <iostream>
#include <array>
constexpr bool istPrim(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) if (n % i == 0) return false;
return true;
}
consteval int quadrat(int x) { return x * x; } // MUSS zur Übersetzungszeit laufen
template <int N>
constexpr auto primzahlen() {
std::array<int, N> a{};
int gefunden = 0;
for (int k = 2; gefunden < N; k++) if (istPrim(k)) a[gefunden++] = k;
return a;
}
int main() {
constexpr auto p = primzahlen<8>(); // komplett vom Compiler berechnet
for (int x : p) std::cout << x << ' ';
std::cout << '\n' << quadrat(12) << '\n';
static_assert(istPrim(97));
return 0;
}2 3 5 7 11 13 17 19 144
Weitere nützliche Sprachmittel
| Mittel | Wirkung |
|---|---|
auto, decltype | Typen ableiten |
nullptr | Nullzeiger (statt NULL/0) |
enum class | typsichere Aufzählung |
using Name = Typ; | Typalias (statt typedef) |
= default, = delete | Spezialfunktionen anfordern/verbieten |
override, final | Überschreiben prüfen / verbieten |
[[nodiscard]] | Rückgabewert darf nicht ignoriert werden |
std::span<T> (C++20) | Sicht auf zusammenhängende Daten (Array, Vektor) |
std::string_view | Sicht auf Text |
<=> (Spaceship) | alle Vergleiche auf einmal |
#include <iostream>
#include <span>
#include <vector>
#include <array>
#include <compare>
#include <string>
enum class Farbe { Rot, Gruen, Blau }; // eigener Namensraum, kein Umwandeln in int
std::string name(Farbe f) {
switch (f) { case Farbe::Rot: return "rot"; case Farbe::Gruen: return "grün"; case Farbe::Blau: return "blau"; }
return "?";
}
double mittel(std::span<const double> werte) { // nimmt Array, vector, std::array ...
double s = 0;
for (double w : werte) s += w;
return werte.empty() ? 0 : s / werte.size();
}
struct Version {
int major, minor;
auto operator<=>(const Version&) const = default; // erzeugt <, >, <=, >=, ==, !=
};
int main() {
std::cout << name(Farbe::Gruen) << '\n';
std::vector<double> v{1, 2, 3, 4};
double a[] = {10, 20};
std::array<double, 3> arr{1, 1, 4};
std::cout << mittel(v) << ' ' << mittel(a) << ' ' << mittel(arr) << '\n';
std::cout << (Version{1, 2} < Version{1, 10}) << (Version{2, 0} == Version{2, 0}) << '\n';
int* p = nullptr;
std::cout << (p == nullptr) << '\n';
return 0;
}grün 2.5 15 2 11 1
Zeit und Zufall
#include <iostream>
#include <chrono>
#include <random>
#include <thread>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
std::this_thread::sleep_for(20ms);
auto dauer = duration_cast<milliseconds>(steady_clock::now() - start);
std::cout << (dauer.count() >= 20) << '\n';
std::mt19937 zufall(42); // fester Startwert: reproduzierbar
std::uniform_int_distribution<int> wuerfel(1, 6);
for (int i = 0; i < 5; i++) std::cout << wuerfel(zufall) << ' ';
std::cout << '\n';
return 0;
}1 3 5 6 2 5
Benutze <random> statt rand(): Es ist besser verteilt und thread-sicher nutzbar. Für Sicherheit (Schlüssel) nimmst du std::random_device oder Betriebssystem-Funktionen.
Module (C++20)
Module (import std;, export module ...;) ersetzen #include und Header: schneller übersetzbar, ohne Makro-Lecks. Die Unterstützung in Werkzeugen (CMake, Compiler) reift noch; neue Projekte können sie bereits ausprobieren. Dieser Kurs bleibt bei Headern, die überall funktionieren.
Merke
- Ranges verketten
filter,transform,takezu lazy Pipelines - Structured Bindings zerlegen Tupel, Paare, Maps und Strukturen
constexpr/constevalrechnen beim Übersetzen;static_assertprüft dortenum class,nullptr,span,string_view,<=>machen Code sicherer und kürzer<chrono>und<random>ersetzentime()undrand()
Aufgabe
Nutze eine Ranges-Pipeline, um aus den Zahlen 1 bis 50 die Quadrate aller durch 3 teilbaren Zahlen zu bilden und die ersten fünf auszugeben.