C++ Kurznotizen
Verfasst: 11.12.2018, 15:30
Hey!
Da ich immer wieder in die Diskussion "Was ist eigentlich neu in C++XY?" oder "Gibt es ein Feature für $x, was empfohlen werden kann?", wollte ich hier mal eine Liste mit Antworten ohne Frage schreiben. Ich freue mich natürlich auch über Beiträge eurerseits, um die Liste weiter zu vervollständigen.
Sprachfeatures
Dirty Hacks und Tricks
Da ich immer wieder in die Diskussion "Was ist eigentlich neu in C++XY?" oder "Gibt es ein Feature für $x, was empfohlen werden kann?", wollte ich hier mal eine Liste mit Antworten ohne Frage schreiben. Ich freue mich natürlich auch über Beiträge eurerseits, um die Liste weiter zu vervollständigen.
Sprachfeatures
- Alternative Operator-Schreibweise: Es gibt für sehr viele Operatoren alternative Schreibweisen, welche meiner Meinung nach die Lesbarkeit des Codes erhöhen:
Ich persönlich verwende für die Bit-Operatoren immer noch die Kurzform, da ich so eine klare Unterscheidung zwischen logischen und arithmetischen Operationen habe.
Code: Alles auswählen
if(a and not b) // a && !b ;
Anmerkung: In MSVC++ muss der Header ciso646 eingebunden werden! (Danke, dot!) - Template-Parameter können einen konstanten Typen besitzen. Damit können einige praktische Konstrukte erzeugt werden:
Code: Alles auswählen
std::pair<const int, int> pair { 1 , 2 }; pair.first = 10; // nicht erlaubt pair.second = 20; // erlaubt
- Structured Bindings erlauben das Dekonstruieren von Tupeln, Arrays und Strukturen in lokale Variablen. Sehr praktisch für mehrere Rückgabewerte:
Für eine Header-only oder statische Funktion bietet sich auch an, return type deduction via auto zu verwenden: (Danke, NytroX!)
Code: Alles auswählen
std::tuple<std::string, int>> get_name_and_age(); auto [ name, age ] = get_name_and_age();
Code: Alles auswählen
auto myfunc(int i) { struct returntype { int result; bool success = true; }; return returntype{ i * 2 }; //geht, weil return type "auto" :-) } int main() { auto test1 = myfunc(10); //normal if(test1.success) { auto x = test1.result; //benutzen } auto [result, success] = myfunc(20); //de-struct-uring return result; }
- constexpr hat neben der Eigenschaft, dass Code zu Compilezeit ausführbar wird, auch die sehr praktische Eigenschaft, dass in einer constexpr-Funktion kein undefined behaviour auftreten darf. Damit kann man schon viele Fehlerquellen zu Compilezeit ausschließen Ist doch nicht so, danke an dot!
- if constexpr() erlaubt es, bestimmte Codepfade zu Compilezeit komplett auszuschließen. Der ausgeschlossene Codepfad darf auch diverse Fehler enthalten, aber keine Syntaxfehler.
Code: Alles auswählen
// aligned beliebige numerische werte auf 16 template<typename T> T align16(T value) { static_assert(std::is_integral_v<T> or std::is_floating_point_v<T>); if constexpr (std::is_integral_v<T> ) // ohne if constexpr würde die funktion nicht mit einem float-wert compilieren return value & T(~0xF); else return 16.0 * floor(value / 16.0); }
- if mit Initialisierer: Ermöglicht es, in der Bedingung eine Variable zu deklarieren, die nur im if- bzw. else-Block verfügbar ist, aber nicht im umschließenden Scope:
Das ganze lässt sich auch mit Structured Bindings verbinden, was insbesondere bei Containern praktisch ist:
Code: Alles auswählen
if(std::optional value = some_func(); value) std::cout << "Ergebnis = " << (*value) << std::endl; else std::cout << "Kein Ergebnis" << std::endl; value = 10; // error: use of undeclared identifier 'value'
Code: Alles auswählen
std::map<key_t, value_t> collection; if(auto [ it, inserted ] = collection.emplace(key, value); not inserted) std::cout << "Fehler beim Einfügen des Schlüssels " << key << "!" << std::endl;
- Leicht verstörend, aber durchaus praktisch: Unicode-Symbole sind in Identifiern erlaubt (seit wann weiß ich nicht, aber es tut):
Code: Alles auswählen
int ➓ = 10; void ツ();
- #include <filesystem> enthält Klassen und Funktionen für die Enumeration und Inspektion von Dateisystemen.
- #include <chrono> enthält Klassen und Funktionen für Zeitmessung.
- #include <regex> enthält eine Implementierung für reguläre Ausdrücke.
- std::byte ist ein nichtartithmetischer Datentyp für "ein Byte", welcher nur Bitoperationen und Vergleiche zuässt.
- std::optional<T> ist ein Datentyp, welcher entweder keinen Wert oder einen Wert vom Typ T enthält
- std::variant<T1, T2, T3, ...> ist eine typsichere C++-Variante für ein C-union. Merkt sich, welcher Datentyp aktuell gespeichert ist und konstruiert/dekonstruiert den Datentyp korrekt.
- std::bitset<N> ist ein Bitset, welches aus einem Integer konstruiert werden kann und auch in einen Integer konvertiert werden. Supportet Shift- und Bitwise-Operatoren zwischen Bitsets.
- std::array<T, N> ist eine C++-Abstraktion eines Arrays. Besitzt die gängigen Containerfunktionen begin(), end(), data(), size(), at(), ... Vorteil: std::array<T,N> ist ein Wertetyp, der Vergleichsoperatoren beherrscht. Kann damit also trivial kopiert werden und zerfällt in Expressions nicht in einen Pointer. Nachteil: Initialisierung mit "beliebiger Werteliste" ist etwas aufwändiger, aber mit class template argument deduction trotzdem einfach initialisiert werden:
Code: Alles auswählen
#include <array> std::array const a = { 1, 2, 3 }; // std::array<int, 3>
Dirty Hacks und Tricks
- assert als Hilfe für "not implemented yet"-Fehler oder ähnliche Debugausgaben mit Absturz.
Das ganze funktioniert prima und gibt nicht mal ein Warning....
Code: Alles auswählen
assert(false); // war gestern assert(false and "not implemented yet"); // ist die Zukunft!