C++ Kurznotizen

Hier können Artikel, Tutorials, Bücherrezensionen, Dokumente aller Art, Texturen, Sprites, Sounds, Musik und Modelle zur Verfügung gestellt bzw. verlinkt werden.
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Antworten
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

C++ Kurznotizen

Beitrag von xq »

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
  • Alternative Operator-Schreibweise: Es gibt für sehr viele Operatoren alternative Schreibweisen, welche meiner Meinung nach die Lesbarkeit des Codes erhöhen:

    Code: Alles auswählen

    if(a and not b) // a && !b
    	;
    
    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.

    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:

    Code: Alles auswählen

    std::tuple<std::string, int>> get_name_and_age();
    auto [ name, age ] = get_name_and_age();
    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

    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:

    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'
    
    Das ganze lässt sich auch mit Structured Bindings verbinden, was insbesondere bei Containern praktisch ist:

    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 ツ();
Standard-Library
Datentypen
  • 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.

    Code: Alles auswählen

    assert(false); // war gestern
    assert(false and "not implemented yet"); // ist die Zukunft!
    Das ganze funktioniert prima und gibt nicht mal ein Warning....
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von xq »

Habe Alternative Operator-Schreibweise und if mit Initialisierer hinzugefügt.
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2348
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Jonathan »

Jup, sind nette und nützliche Sachen dabei :) Muss ich bei nächster Gelegenheit dann mal ausprobieren.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Krishty »

Vieles davon lehne ich aus Mental-Load-Gründen ab; die alternativen Operator-Schreibweisen finde ich aber tatsächlich in einem Fall sehr nützlich: Die Negation kann damit endlich deutlich ausgedrückt werden.

if(!isFinite) ← das ! überliest man zu oft

if(false == isFinite) ← Intention viel deutlicher – aber PITA, das immer auszutippen

if(not isFinite) ← endlich
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von xq »

Neue Kategorie mit kleinen Hacks, die einem das Leben vereinfachen.

Bzgl. den Operatoren: Die waren auch tatsächlich der Auslöser, warum ich den Thread angefangen habe. So gut und keiner wusste davon
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Krishty »

Ich freue mich jedenfalls auf deine Kommentare zum Spaceship-Operator für Three-Way Comparison (a <=> b) ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: C++ Kurznotizen

Beitrag von NytroX »

Mehrfache, benannte return values (a.k.a. Voldemort types): "nie wieder tuples!"

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;
}
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von dot »

Beachte: Wenn du unter Visual C++ die alternative operator tokens verwenden willst, muss du entweder mit /Za oder /permissive- kompilieren oder den header <ciso646> includen. In C musst du afaik immer <iso464.h> includen um die alternative tokens zu bekommen…
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von dot »

MasterQ32 hat geschrieben: 11.12.2018, 15:30[*] 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
Das stimmt so leider nicht. Überhaupt: undefined behavior hat von vornherein die nette Eigenschaft, dass es auch außerhalb einer constexpr function nicht auftreten "darf". ;) Der constexpr Specifier bietet keinen magischen Schutz vor undefined behavior (UB), im Gegenteil, es gibt Situationen in denen man sich erst durch die Präsenz von constexpr UB einhandeln kann. (OK, genaugenommen ist das hier ein Fall der nicht explizit UB ist, sondern der Compiler darf das Programm ohne Fehler kompilieren und es ist nicht definiert, was das Verhalten des resultierenden Programmes sein sollte; läuft in der Praxis aber ziemlich genau aufs Gleiche raus…)

Was stimmt ist, dass in einer core constant expression kein UB auftreten darf. Beachte: constant expression != constexpr function. Der constexpr Specifier auf einer Funktion tut nichts anderes als sicherzustellen, dass ein paar Grundvoraussetzungen erfüllt sind ohne die die jeweilige Funktion von vornherein nicht in einer core constant expression aufgerufen werden könnte (abgesehen davon macht er die Funktion inline). Es ist erlaubt eine constexpr function in einer core constant expression zu verwenden. Aber nicht jeder Aufruf einer constexpr function muss Teil einer core constant expression sein. Und nicht jede expression die eine core constant expression ist, wird auch zur Compiletime ausgewertet. Es ist lediglich so, dass in Kontexten, wo eine expression zur Compiletime ausgewertet werden muss, nur eine expression verwendet werden kann die eine constant expression ist. So lange ein Aufruf deiner constexpr function nicht in einer Expression passiert die tatsächlich zur Compiletime ausgewertet wird, handelt es sich um nichts anderes als einen stinknormalen Funktionsaufruf (den der Compiler unter der as-if rule wegoptimieren kann, wenn ihm danach ist, wie jeden anderen Funktionsaufruf auch). Es hindert dich niemand daran, eine Funktion zu schreiben, die die Voraussetzungen für constexpr erfüllt aber in manchen Fällen etwas tut was in einer core constant expression nicht erlaubt wäre, z.B. irgendwas, was zu UB führt. So lange das UB nicht tatsächlich während der Auswertung einer core constant expression zur Compiletime angelaufen wird, gibt es keine Garantie dass du irgendwas von der Präsenz des UB mitbekommst. Wenn du das UB nur in einem Aufruf anläufst der nicht zur Compiletime ausgewertet wird, dann hast du einfach nur UB und fertig.

Rein prinzipiell: Einer der Grüde wieso es UB überhaupt erst gibt ist dass viele Dinge für den Compiler praktisch unmöglich zu diagnostizieren sind, z.B. weil die Diagnose effektiv äquivalent zur Lösung des Halteproblems wäre. Die Forderung dass UB in einer constexpr function allgemein zu einem Compilerfehler führt, würde die Implementierung eines standardkonformen C++ Compilers praktisch unmöglich machen…
Zuletzt geändert von dot am 03.02.2019, 02:03, insgesamt 1-mal geändert.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von xq »

Krishty hat geschrieben:Ich freue mich jedenfalls auf deine Kommentare zum Spaceship-Operator für Three-Way Comparison (a <=> b) ;)
Den hab ich mir noch nicht genauer angeguckt, kommt ja auch erst in Zukunft.
Nytrox hat geschrieben:Mehrfache, benannte return values (a.k.a. Voldemort types): "nie wieder tuples!"
Auch ne gute Idee, werde ich mal in die Liste aufnehmen!
dot hat geschrieben:Beachte: Wenn du unter Visual C++ die alternative operator tokens verwenden willst, muss du entweder mit /Za oder /permissive- kompilieren oder den header <ciso646> includen. In C musst du afaik immer <iso464.h> includen um die alternative tokens zu bekommen…
Ist irgendwie schräg implementiert, da ja das Header-File effektiv nicht geparsed wird und das als Sonderfall im Compiler implementiert ist. Hat mich auch schon gewundert...

dot hat geschrieben:<-- Ausführungen über constexpr+UB -->
Danke für die ausführliche Erklärung! Hatte das in einem CppCon-Vortrag so gehört und mal als "korrektes Wissen" übernommen. Aber stimmt natürlich, wäre das Haltproblem... *Schnüff* Habe den Beitrag oben hingehend angepasst
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von dot »

MasterQ32 hat geschrieben: 02.02.2019, 09:50
dot hat geschrieben:Beachte: Wenn du unter Visual C++ die alternative operator tokens verwenden willst, muss du entweder mit /Za oder /permissive- kompilieren oder den header <ciso646> includen. In C musst du afaik immer <iso464.h> includen um die alternative tokens zu bekommen…
Ist irgendwie schräg implementiert, da ja das Header-File effektiv nicht geparsed wird und das als Sonderfall im Compiler implementiert ist. Hat mich auch schon gewundert...
Der Header wird sehr wohl geparsed. Im Falle von C definiert <iso464.h> einfach nur Makros (vom C Standard so vorgegeben) die die entsprechenden Operator Token zu den "normalen" Operatoren expanden. In Standard C++ sind die Token built-in und es wird kein zusätzlicher Header benötigt. Der Header <ciso646> ist auch (voraussichtlich mit C++ 20) nicht Teil der Standard C++ Library; der Header <iso646.h> ist in C++ per Definition leer und nur als deprecated Compatibility-Feature vorhanden. <ciso646> wird nur unter Visual C++ by default benötigt (included einfach nur <iso646.h>, welcher dann nur im Falle von C Modus oder wenn die _MSC_EXTENSIONS an sind die entsprechenden Makros definiert). Sofern man aber z.B. mit einer aktuellen Version von Visual Studio arbeitet und mit /permissive- kompiliert (schaltet den Compiler in den C++ Standard Conformance Modus, is mittlerweile bei neuen Projekten by default an; IMHO sollte niemand mehr ohne) dann wird der Header nicht benötigt da die Token dann auch dem Visual C++ Compiler als built-in bekannt sind. Wollte nur darauf hinweisen, dass man es in Visual C++ braucht, so lange man nicht mit entsprechenden Flags arbeitet. Mein Rat wäre aber, einfach immer /permissive- zu verwenden, dann braucht man den Header auch in Visual C++ nicht. Sofern man aus irgendeinem Grund nicht mit /permissive- arbeiten kann, sollte man das #include <ciso646> vermutlich in ein #ifdef _MSC_VER wrappen, um den Code möglichst future-proof und portabel zu halten…
Benutzeravatar
Schrompf
Moderator
Beiträge: 4831
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Schrompf »

MasterQ schrieb das übrigens nur, weil ich mit VC2017 in genau diese Falle getappt bin. Ich schleife meine Projektfiles aber seit sicher 10 Jahren von Version zu Version - am Ende ist bei mir dieses Flag noch nicht aktiv. Danke für die Erklärungen!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Krishty »

Schrompf hat geschrieben: 02.02.2019, 22:19 Ich schleife meine Projektfiles aber seit sicher 10 Jahren von Version zu Version - am Ende ist bei mir dieses Flag noch nicht aktiv. Danke für die Erklärungen!
Dann fehlen dir wahrscheinlich auch andere Einstellungen – auf der Maloche wurden beim Upgrade nicht die neuen Debug-Informations-Formate übernommen; die Pfade standen noch auf Output-in-Projektverzeichnis statt Output-in-Solution-Verzeichnis, usw. Am besten mit VS 2017 alle Projekte einmal neu anlegen, so weh es auch tut.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Krishty »

dot hat geschrieben: 02.02.2019, 21:54Mein Rat wäre aber, einfach immer /permissive- zu verwenden
Frage dazu: Wenn ich mit experimentellem Modules-Support kompiliere, sind import, module & Co. plötzlich Schlüsselwörter. Da ich die hundertfach in meinem Code als Variablen-, Funktions-, und Makro-Namen benutzt habe, durfte ich erstmal einen Tag lang aufräumen. Haben die für Module, wie sie aktuell aussehen, tatsächlich so übel mit Legacy-Code gebrochen?! Oder sollen die Schlüsselwörter später Kontext-sensitiv sein?!

Sonst muss man neben /permissive- nämlich auch den Modules-Support einschalten um nicht heiter Code zu schreiben, der mit dem nächsten Standard kaputtgeht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von dot »

Krishty hat geschrieben: 03.02.2019, 19:26Haben die für Module, wie sie aktuell aussehen, tatsächlich so übel mit Legacy-Code gebrochen?! Oder sollen die Schlüsselwörter später Kontext-sensitiv sein?!
Momentan sieht es auf jeden Fall so aus, dass die Keywords für Modules context-sensitive sein werden: P0924r1
Benutzeravatar
Krishty
Establishment
Beiträge: 8227
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: C++ Kurznotizen

Beitrag von Krishty »

Das beruhigt mich; danke :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten