EMZETT.
Login

Kurz: int addiere(int a, int b) { return a + b; }

Teil des Kurses C++

Kapitel 4 von 15 im Kurs C++. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

#include <iostream>
#include <string>
 
int addiere(int a, int b) { return a + b; }
 
double potenz(double basis, int exponent = 2) {          // Standardwert
    double r = 1;
    for (int i = 0; i < exponent; i++) r *= basis;
    return r;
}
 
// Überladen: gleicher Name, andere Parameter
std::string beschreibe(int x) { return "int " + std::to_string(x); }
std::string beschreibe(double x) { return "double " + std::to_string(x).substr(0, 4); }
std::string beschreibe(const std::string& x) { return "string " + x; }
 
// Mehrere Rückgabewerte über struct oder Tupel
struct Ergebnis { int quotient, rest; };
Ergebnis teile(int a, int b) { return {a / b, a % b}; }
 
int main() {
    std::cout << addiere(3, 4) << ' ' << potenz(3) << ' ' << potenz(2, 10) << '\n';
    std::cout << beschreibe(5) << ", " << beschreibe(2.5) << ", " << beschreibe(std::string("hi")) << '\n';
    auto [q, r] = teile(17, 5);
    std::cout << q << " Rest " << r << '\n';
    return 0;
}

Ausgabe:

7 9 1024
int 5, double 2.50, string hi
3 Rest 2

Parameterübergabe

FallSchreibweise
kleiner Wert, nur lesenint x, double d
großes Objekt, nur lesenconst std::string& s
Objekt ändern (Ausgabeparameter)std::vector<int>& v
Objekt übernehmen und behaltenstd::string s + std::move (siehe Move-Semantik)
optionaler Parameterstd::optional<T> oder Standardwert

Lambdas

Ein Lambda ist eine namenlose Funktion, die direkt an der Stelle geschrieben wird, wo man sie braucht:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
 
int main() {
    auto quadrat = [](int x) { return x * x; };
    std::cout << quadrat(7) << '\n';
 
    std::vector<int> z{5, 3, 9, 1, 7};
    std::sort(z.begin(), z.end(), [](int a, int b) { return a > b; });     // absteigend
    for (int x : z) std::cout << x << ' ';
    std::cout << '\n';
 
    int schwelle = 4;
    auto groesser = [schwelle](int x) { return x > schwelle; };            // [schwelle]: Kopie "eingefangen"
    std::cout << std::count_if(z.begin(), z.end(), groesser) << '\n';
 
    int zaehler = 0;
    auto zaehle = [&zaehler]() { return ++zaehler; };                      // [&zaehler]: Referenz
    zaehle(); zaehle();
    std::cout << zaehler << '\n';
 
    std::function<int(int)> fib = [&fib](int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); };
    std::cout << fib(15) << '\n';
 
    auto allgemein = [](auto a, auto b) { return a + b; };                 // generisches Lambda
    std::cout << allgemein(1, 2) << ' ' << allgemein(1.5, 2.5) << '\n';
    return 0;
}

Ausgabe:

49
9 7 5 3 1
3
2
610
3 4

In den eckigen Klammern steht, was das Lambda von außen einfängt: [] nichts, [x] Kopie, [&x] Referenz, [=] alles als Kopie, [&] alles als Referenz.

constexpr: Berechnung zur Übersetzungszeit

#include <iostream>
#include <array>
 
constexpr int fakultaet(int n) { return n <= 1 ? 1 : n * fakultaet(n - 1); }
 
int main() {
    constexpr int f5 = fakultaet(5);               // wird vom Compiler berechnet
    std::array<int, fakultaet(4)> feld{};          // Größe zur Übersetzungszeit bekannt
    static_assert(fakultaet(3) == 6, "Rechenfehler");
    std::cout << f5 << ' ' << feld.size() << '\n';
    return 0;
}

Ausgabe:

120 24

constexpr verlagert Arbeit vom Programmlauf zum Übersetzen. static_assert prüft Bedingungen schon beim Kompilieren.

Funktionen höherer Ordnung

#include <iostream>
#include <functional>
#include <vector>
#include <string>
 
std::function<int(int)> malGlied(int faktor) {
    return [faktor](int x) { return x * faktor; };
}
 
template <typename F>
void fuerJeden(const std::vector<std::string>& l, F f) { for (const auto& s : l) f(s); }
 
int main() {
    auto dreifach = malGlied(3);
    std::cout << dreifach(14) << '\n';
    fuerJeden({"a", "b"}, [](const std::string& s) { std::cout << "[" << s << "]"; });
    std::cout << '\n';
    return 0;
}

Ausgabe:

42
[a][b]

noexcept, [[nodiscard]] und Attribute

  • noexcept: Die Funktion wirft keine Ausnahme (hilft dem Compiler beim Optimieren)
  • [[nodiscard]]: Der Rückgabewert darf nicht ignoriert werden (wichtig bei Fehlercodes)
  • [[maybe_unused]], [[deprecated("Text")]], [[fallthrough]]

Merke

  • Standardwerte und Überladen erleichtern Aufrufe; große Parameter als const&
  • Mehrere Ergebnisse: struct oder std::tuple mit Structured Bindings
  • Lambdas: [Capture](Parameter) { Rumpf }; [=] kopiert, [&] verweist
  • constexpr rechnet zur Übersetzungszeit, static_assert prüft dort
  • std::function speichert beliebige aufrufbare Objekte

Übungsaufgabe

Sortiere einen Vektor von Strings nach Länge und alphabetisch mit einem Lambda.

Quiz zur Selbstkontrolle

in einem Lambda?

  • Alle benutzten Variablen werden als Referenz eingefangen (richtig)
  • Nichts wird eingefangen
  • Alles wird kopiert
  • Das Lambda ist rekursiv

Weiter im Kurs

Zurück: Bedingungen und Schleifen

Weiter: Strings und Container der Standardbibliothek

Alle Kapitel: C++ im Überblick