Nov 10, 2020
- Seite 47 (u.a.): Fibonacci ist um eins
verschoben – fügen Sie ein
=
ein: return n<=2 ? 1 : fib(n-2)+fib(n-1)
- Seite 65: Für 0 ist das Ergebnis falsch. Es fehlt entweder eine Überprüfung
der Benutzereingabe auf
wert>0
oder besser eine Überprüfung des Sonderfalls n==0
in berechne(int n)
.
Aug 01, 2020
- Seite 15:
map<string<,vector<int>>
- Seite 133, letzte Textzeile: “In
main
wird die aber bei opf.return reset(nullptr)
…”
- Seite 139, Listing 19.2: “
Base *p = new Base Derived {}
“
- Seite 161, Listing 22.4: “
return Thing(forward<T>(t), forward<TU>(u), forward<TV>(v));
“
- Seite 172, letzter Absatz: “…weil das Element
data_ copyOnly_
nicht verschiebbar ist.” (der Fettdruck in Listing 23.9 ist korrekt.)
- Seite 225, drittletzte Zeile: “Mit ~~deiner~~ [seiner]{.underline} Hilfe”
- Seite 265, zweitletzte Zeile des ersten Abschnittes: “Design für ~~einen~~ [eine]{.underline} gute Hashfunktion”
- Seite 288, letzte Zeile: “einer von ihnen ~~das~~ [der]{.underline} Eigentümer”
- Seite 290, Abbildung 43.2: Nur “a” ist abgekoppelt, das Objekt (grauer Block) existiert noch. Doch ist dies nun keine so gute Veranschaulichung des Textes mehr das Beispiel und die Abbildung müssen komplett angepasst werden.
- Seite 302, 2. Zeile & Seite 305, Ende: “…
string
und dequeue
…”
- Seite 306, zweite Zeile im zweiten Abschnitt: “Einen Algorithmus ~~Sie können~~ [können Sie]{.underline} schlecht”
- Seite 307, Mitte: “…
mem_func_ref
…”
- Seite 308: “In der Computerwissenschaft nennt man das ~~Currying~~ [partielle Funktionsanwendung]{.underline}.”
- Seite 310, Listing 47.5, erste Kommentarzeile: “definiert ~~einen~~ einen Funktor”
- Seite 343, Listingunterschrift 53.3 und Absatz darunter: “~~Transparente~~ Operationen [wie gewohnt]{.underline} auf den gekapselten Wert”, sowie “Durch eine automatische Typumwandlung von und nach
string
~~ist~~ [erlaubt]{.underline} das ~~für den~~ [dem]{.underline} Benutzer ~~transparent~~ [eine wie gewohnte Verwendung]{.underline}.”
- Seite 354, dritte Zeile: “Kapitel ~~49~~ [53]{.underline}”
- Seite 359, 7. Zeile: “Für eine[n]{.underline} durchschnittlichen Ausdruck”
- Seite 359, Fußnote: Der Algorithmus wandelt den Ausdruck beim Optimieren in einen deterministischen endlichen Automaten (deterministic finite automata, DFA) um. Das kann im schlimmsten Fall ~~quadratisch~~ [exponentiell]{.underline} viel Zeit und Speicher benötigen~~.~~[, im Normalfall jedoch deutlich weniger.]{.underline}
- Seite 362, Beispiel “und
ratio<64,12>::den == 12 3
“
- Seite 366, Zeile nach Listing 57.4: “Würde links von der Zuweisung minutes
min1 stehen =
“
- Seite 373, zweite Zeile: “Mit der ~~Sie können~~ [können Sie]{.underline}”
- Seite 373, Seitenmitte: “Doch die Ansprüche aus ~~dem~~ [den]{.underline}”
- Seite 374, erste Zeile: “die dann [wie gewohnt]{.underline} ~~transparent~~ in den anderen Komponenten”
- Seite 375, nach Listing 59.3: “3 [GHz]{.underline} ~~Ghz~~”
- Neues Kapitel [lib.string] Die neuen
string
-Bibliotheksfunktionen to_string
und stoXX
-Varianten werden erklärt (XX
aus i
, l
, ll
, etc.).
- Neues Kapitel [lib.c99] Es gibt noch einige Funktionen aus C99 zu besprechen, wie
fma
, llround
, etc.
- Neues Kapitel zu
inline namespace
- Neues Kapitel zu neuen Stream-Manipulatoren
get_money()
, put_money()
, get_time()
und put_time()
- Korrektur in Listing http://cpp11.generisch.de/compile-log/src/stl-f-bind.html,
/size()`
sollte erst nach double
gecastet werden.
Feb 15, 2018
- S. 30, Satzfehler [(nur 1. Auflage)]{style=”color: #aaa;”}
- Mir sind an einigen Stellen “~~\newline~~” in den Text geraten, die dort nicht hingehören.
- S. 63, Listing 4.2, Tippfehler [(nur 1. Auflage)]{style=”color: #aaa;”}
if(!cin) {
muss sein if(!std::cin) {
.
- S. 63, Text unter Listing unten, Fehler [(nur 1. Auflage)]{style=”color: #aaa;”}
- Auf dem Mac habe ich hier mal “~~0~~123” bekommen, auf einem Linux-System “~~0~~321” – und beides ist richtig.
- S. 82, Kasten, Tippfehler [(nur 1. Auflage)]{style=”color: #aaa;”}
#include
- S. 152, Kasten, Verbesserung
#include <ifstream>
- S. 487, Fehler: “==” statt “!=”
- “Das könnte ich mit
if(groesstes !== nullptr)
tun. Das kann man aber zu if(groesstes)
abkürzen.”
- S. 491, Fehler
- “Dann setzen Sie
new
ein, um das Objekt auf dem ~~Stack~~ [Heap]{.underline} zu erzeugen.”
- S. 510, Tippfehler
- “Das C-Array
haello
ist drei Zeichen lang....”
- S. 512, Fehler
- ”…für diesen Zweck eine Funktion Namens
difference distance
aus…”
- S. 839, Tabelle 28.7, Satzfehler [(nur 1. Auflage)]{style=”color: #aaa;”}
- Die Elemente der dritte Spalte der Tabelle müssten in umgekehrter Reihenfolge sein.
Oben zu “yocto” sollte also “[10^-24^?]{.underline}” stehen und unten zu “yotta” also “[10\^^24^?]{.underline}”.
Geplante Ergänzungen
std::visit
für std::variant
(C++17)
Dez 09, 2014
Seite 50: #include, da fehlt das “l”.
Kapitel 14.1, Fehlerbehandlung mit Rückgabewerten
Im Buch steht in Listing 14.1, Seite 207:
Zeile 12: int count = 0 // am EOF wird noch ein Wort gelesen
Zeile 18: return count - 1;
wobei im Beispiellisting 28-codes.cpp
aber steht
Zeile 12: int count = -1 // am EOF wird noch ein Wort gelesen
Zeile 18: return count;
Beides ist korrekt, nur anders formuliert.
S. 138, Listing 10.4
iota
kommt aus dem Header numeric
, nicht algorithm
.
#include <array>
#include <algorithm>
#include <numeric>
S. 170, Prüfen des Erfolgs des Stream-Ins
!
bindet stärker als >>
. An dieser und anderen Stellen muss der boolsche Teilausdruck geklammert werden:
if(!(std::cin >> variable))
.
S. 187, Listing 12.17, Kommentar bezieht sich auf falsche Variable
// raus aus innerer xy-Schleife
S. 188, falscher Bezug auf Source-Stelle
Hätten Sie bei cout << prod// Ziel von break
ein break
eingebaut, dann würde jedoch die x-Schleife verlassen und bei cout << "\n"
fortgefahren.
S. 294, falscher Bezug auf Variable
Weil aber die arrSPAET
-Definition nach der Verwendung steht,
S. 314, Listing 20.10, Parameter fehlt
Hier fehlt der Stream als Parameter (oder die globale Variable). Außerdem sind meine internen Prüfkommentare “//=” hineingerutscht (Auszeichnung des Listings als C++-Programm verkehrt).
//... Basis2 und Wert2 wie gehabt ...
void ausgabe(Basis2 x, ostream& os) {
// Übergabe als Wert
x.print(os);
}
int main() {
Basis2 ba2{}; ausgabe(ba2, cout); // gibt 8 aus
//= 8
Wert2 we2 {}; ausgabe(we2, cout); // gibt auch 8 aus
//= 8
}
S. 314, falsches Ergebnis
dann hätte ausgabe(we2)
die ~~11~~[10]{.underline} ausgegeben
Nov 18, 2014
Nein, man lernt tatsächlich nie aus. Seit C++11 können wir ja schreiben
vector data{};
...
for(auto e : data) { cout << e; };
Das ist schon ganz schön praktisch gegenüber for(vector it=data.begin();...) { cout << *it; }
, hat aber den großen
Nachteil, dass erstens e
hier oimmer kopiert wird und zweitens Änderungen an e
sich nicht in data
auswirken.
Stimmt, und deshalb erzähle ich auch immer, dass man daran danken sollte, in solchen Schleifen zuerst immer zu prüfen,
ob man stattdessen nicht auto&
oder const auto&
verwenden sollte:
vector<int> data{};
...
for(auto& e : data) { e += 1; };
Das verhindert Kopie und data
wird auch wirklich modifiziert.
Jetzt werde
ich gerade erinnert
, dass das nicht immer reicht. Zum Beispiel kann es keine (echte) Referenz in einen vector<bool>
geben, weil man
einzelne Bits nicht adressieren (also referenzieren) kann. Das gäbe mit auto&
einen Fehler.
Daher, jetzt meine neue Regel: Man probiere es mit auto&&
. Das ist dann der Einsatz der
Meyerschen Universellen Referenzen, soweit
ich das erkennen kann:
vector<int> data{};
...
for(auto&& e : data) { e += 1; };
Im Effekt bekommt man eine &
, wenn nötig und eine &&
, wenn erlaubt. Die genaue Erklärung was passiert, folgt in
einem Detailbeitrag. Bis dahin probiere ich diese neue Merkregel mal aus.
Nachtrag
In einem Vorschlagsdokument des C++-Standards steht das Dilemma beschrieben:
What should we do about this problem? We can teach users to write
for (auto& elem : range)
or for (const auto& elem : range)
, but they’re
imperfect (for proxies or mutation, respectively), and they require novices to
be introduced to auto and references earlier than otherwise necessary.
As explained immediately below, for (auto&& elem : range)
is superior, but
it’s terrible to teach to novices. Even experienced C++98/03 programmers can
find it difficult to learn how rvalue references, the template argument
deduction tweak, and reference collapsing interact to produce what Scott Meyers
calls “universal references” (see [1]), and auto&& is far less commonly seen
than T&&
in perfect forwarding.
Andererseits sagt Herb Sutter
in Back To Basics
, dass man auto&&
nicht für lokale Variablen verwenden soll. Da stimme ich im Prinzip auch zu, aber grüble, ob sein
Gesagtes nicht bei diesen auto-for-Schleifen eine Ausnahme sind. Hauptsächlich im Bezug der Einfachheit allerdings.
“STL” gibt etwas mehr Erklärung hier und bezieht auch den
kommenden C++-Standard (wahrscheinlich) mit ein, der…
Ausblick
…eine neue Syntax enthält, die genau diese Schreibweise abkürzt: Einfach den gesamten Typen in der
Schleifendeklaration weglassen und der Compiler macht daraus automatisch auto&&
:
vector<int> data{};
...
for(e : data) { e += 1; };
…in der (fernen) Zukunft (vielleicht).
Zusammenfassung
Mit all den Informationen, die ich nun habe, möchte es noch einmal zusammenfassen. Vor allem “STL” (oben) und Howard Hinnant konnten mir Dinge klären
Ab C++17 gilt: Verwende for(e :...)
wann immer möglich.
vector<int> data{};
...
for(e : data) { e += 1; };
Bis dahin, mit C++11 alleine, gilt:
In 99.9% der Fälle und somit für alle Entwickler bis zum braunen Gürtel gilt (immernoch): Verwende for(auto &x: ...)
oder for(const auto &x: ...)
wenn Du kannst und kopiere nur wenn nötig:
vector<int> data{};
...
for(auto &e : data) { e += 1; };
...
for(const auto &e : data) { cout << e; };
Entwickler mit dem schwarzen Gürtel, und/oder innerhalb einer Bibliothek mit einem Funktionstemplate für den Typ
von data
, kann es sinnvoll sein for(auto &&x: ...)
zu verwenden:
vector<int> data{};
template
void func(T&& data) {
...
for(auto &&e : data) { e += 1; };
...
}
Denn ob RValue-Ref oder LValue-Ref ist nur dann relevant, wenn data
für seine Iteratoren (temporäre) Proxy-Objekte
benötigt (wie vector<bool>
). Selbst wenn man dieses Konstrukt außerhalb eines Funktionstemplate verwendet, wird
man &&
nicht brauchen, sondern kann sich bewusst zwischen ~[const]~ auto& und [const] auto
entscheiden.
Nov 06, 2014
FAQ
Habe soweit das Programm modern102.cpp in Visual Studio 2012 übernommen. Wenn ich das Prgramm ausführen lassen möchte, bekomme ich als Fehlermeldung:
error C2601: ‘table’: Lokale Funktionsdefinitionen sind unzulässig
Mir wird genau an der Stelle:
…static std::map\<int, int> table{};…
bei der geschweiften Klammer angezeigt, dass was nicht stimmt. Bei genaueren Hinsehen in Ihrem Buch, stehen unter den beiden geschweiften Klammern {} jeweils ein Punkt.
Das klingt sehr danach, dass der Compiler C++11-Konstrukte zurückweist.
Hier sehen Sie
dass “Visual Studio 2012” zum Beispiel die “Initializer Lists” nicht kennt, also die
Initialisierung mit “{…}”. Das ist hier das Problem.
Im Anhang können Sie alternative Schreibweisen für C++11-Konstrukte finden, die Ihr Compiler nicht kennt. Konkret schreiben Sie statt
static std::map<int, int> table{};
in MSVC2012:
static std::map<int, int> table;
Wenn Sie die Möglichkeit haben, sollten Sie auf die neueste Version von Visual Studio wechseln. Wenn nicht, müssen Sie teilweise auf alternative Schreibweisen ausweichen.
Möglicherweise hilft auch ein kleines Update, das habe ich aber nicht selbst probiert.
In early November 2012, Microsoft announced the Visual C++ Compiler November 2012 CTP, which adds more C++11 functionality to Visual Studio 2012.
Im buch können Sie Punkte unter den Klammern sehen, die auf ein C++11-Feature hindeuten (wird in Abschnitt 1.2, “Verwendete Formatierungen”, S. 27, eingeführt). Wenn an solchen Stellen etwas schief geht und der Compiler meckert, sollten Sie skeptisch werden. Hier ist die “Initialisierungsliste” gepunktet unterstrichen – weil “{}” so wenig Text ist, erschient diese Linie als nur zwei Punkte.
Aug 31, 2014
Soso, der C++14-Standard wurde also einstimmig angenommen. Ende des Jahres werden wir also das Update kaufen können.
Natürlich kann man auch den kostenlosen letzten “Working Draft” nehmen,
aber ein paar kleine Änderungen sind da nicht enthalten.
Wie zum Beispiel, dass das von C kommende rand() als Funktion nicht mehr
verwendet werden soll – diese ist jetzt “deprecated”. Stattdessen wird die Verwendung der C++-Template-Sammlung aus **
\<random>** empfohlen.
Ich muss dazu mal ein konkretes Beispiel raussuchen.
Aug 15, 2014
Ich bin auf eine Liste von Namen
für Programmierparadigmen gestoßen, die
wir alle kennen, aber denen köstliche und einprägsame Namen gegeben wurden:
- Die Joda-Bedingung
if(value.equals("Wert"))
` ersetzt if("Wert".equals(value))
- Pokemon Exception Handling wenn man einfach mit
try { block } catch(...) { handler }
alles fängt.
- Egyptian brackets ist, enn die then-
{
-Klammer noch auf die Zeile des if
s geschrieben wird
- Aber am schönsten für strictly typed C++-Liebhaber finde ich die stringly typed Sprachen PHPs oder JavaScript
Mai 21, 2014
Hier wird argumentiert, dass in Java extends
“böse” ist.
Während die Formel “ist böse” natürlich zu einfach ist, stimme ich der Argumentation jedoch zu und ich finde es schön begründet. Mal weniger theoretisch als auch schön an praktischen Beispielen.
In C++ entspricht das wohl dem, dass man besser type traits statt Vererbung verwendenden sollte. Ich sollte den Artikel mal versuchen, nach C++ zu übersetzen. Auf die Schnelle kann ich nur sagen, dass es ja in C++ keine wirklichen Interfaces gibt wie in Java, die man mit “implements” in seinen Klassen verwenden würde. In C++ ist es sehr wenig verbreitet mit puren “Signatur-Klassen” zu arbeiten (alle Methoden abstrakt, i.e. pure virtual). Daher kommt dem “implements” von Java in C++ der Weg über Typparameter von Templates am nächsten, i.e. type traits.
Aber eben nicht komplett, “implements” kann mehr – lesbare Fehlermeldungen zum Beispiel. Und unter anderem deshalb erwarten wir alle Concepts Lite…
Mai 15, 2014
Was ich hier einem Freund
über George R. Martin
geschrieben habe will ich euch nicht vorenthalten. Ich hoffe es kommt auch unübersetzt rüber.
Actually, I can totally follow that reasoning. Not that I think of it I myself have the most proliferating
writing sessions bereft of my internet connection: Either in the train or in a beer garden. That is not bereft
of distractions, mind you. I especially love watching people on the seats and benches when I look up.
Oh well, while George uses Wordstar 4.0, I can top that: My emacs is the ultimate non-distractor, I can tell you.
I have to admit that an open internet connections is the factor offering too many distractions.
Alas, I can not always choose that fruitful environment. For one, I have to use stackoverflow occasionally
(and no offline backup if it is available, yet) and for the other, after three beer it is more efficient
to stop writing then to correct the produced sermon later.
Update: I had to disable comments on this page (spam, spam, spam). If you have one, don’t hesitate to send me an EMail, I will gladly put your comment here. Sorry or the inconvenience.
Feb 21, 2014
Ist es nicht ein Segen, dass man eine eigene neue Klasse auf sooo viele Arten Initialisieren kann?
struct Person {
string name_;
int alter_;
string ort_;
};
Nur 3 Membervariablen, aber 4 tolle Möglichkeiten zum Initialisieren:
Person otto {"Otto", 44, "Aachen"};
Person hans {"Hans", 33};
Person paul {"Otto"};
Person kurt {};
Weil wir die {}
hier verwenden werden alle Member initialisiert – die “überzähligen” Wert-Initialisiert”. Also immer
mit {}
initialisieren, bitteschön, denn Person xxx;
wird *nicht initialisiert. Iih-ba!
Aber ist das überhaupt gut so? Warum sollte man nur einige daten von Person
initialisieren wollen? Gut, ich kann mir
Szenarien vorstellen, wo man mit der Wert-initialierung zufrieden ist, aber alle diese Möglichkeiten zulassen? Wenn
der Datentyp so gedacht ist, dass man mit 0, 1, 2 oder 3 Parametern initialisieren kann, wenn das Sinn ergibt, dann kann
ich auch die entsprechenden Konstruktoren dafür bereitstellen. Das ist dann expliziter, und der Leser der Datenstruktor
muss nicht überlegen “ist das überhaupt so gedacht?”.
Initialisierung von Typen mit private Daten
Daher, warum nicht gleich private Daten haben? Das verhindert nämlich auch die Wert-Initialisierung. Hier kommt dann
höchstens die Null-Initialisierung ins Spiel. Aber der Name ist egal, wir kennen alle den Effekt:
“Wenn Sie keinen Konstruktor selbst definieren, dann erzeugt der Compiler den Default-Konstruktor.”
Dankeschön. Und was bedeutet das? Man hört, der macht gar nichts. Ja, fast. Der macht (manchmal) nichts, aber vorher,
da passiert das Interessante.
Aber sehen wir uns ein Beispiel an.
class Rect {
int area_; // private Daten
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
};
Da Sie von außen nicht mehr direkt auf diese Membervariable zugreifen dürfen, können Sie sie auch nicht mehr einfach mit
geschweiften Klammern {...}
befüllen. Sie können nicht Rect r{1,2};
oder Rect s{6,2,3};
– durch das Packen
von area_
in den privaten Bereich der Klasse ist Wert-Initialisierung für Rect
unmöglich.
Sie benötigen also auf jeden Fall einen Konstruktor. Wir akzeptieren niemals, dass etwas nach der Definition
uninitialisiert herumliegt! Das sieht der Compiler zum Glück auch so und daher generiert in diesen Fällen einen
Konstruktor, nämlich den ohne Parameter. Dadurch können (und sollten) Sie zumindest Rect t{};
verwenden.
Und was macht dieser generierte Konstruktor? Effektiv initialisiert er Ihr Objekt mit Nullen, bzw. bei Membervariablen
komplexerem Typs einem passenden Äquivalent.
Durch so viel Automatismen muss man erst einmal durchsteigen. Denken Sie auch an die Leser Ihres Programms und liefern
Sie auch einen Konstruktor mit, und sei es nur der Konstruktor ohne Parameter. Andererseits, wenn der ohnehin nur tut,
was der Compiler auch generieren würde, dann können Sie dessen Erzeugnis mit =default
anfordern – dann haben Sie es
wenigstens explizit gemacht und der Leser sieht direkt >>achja, Initialisierung ohne Parameter.\<\<.
class Rect {
int area_; // private Daten
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
Rect() = default; // Compiler Konstruktor generieren lassen
};
class Pow {
int result_; // private Daten. hält 'base' hoch 'exp'.
public:
int base_, exp_;
void set(int b, int e) { /* ... */ }
int calc() { return result_; }
Pow() : result_{1} {} // base_, exp_ wurden 0, dann muss result_=1 werden.
};
Während Rect() = default;
nur die Null-Initialisierung anfordert, die der Compiler sowieso eingesetzt hätte, macht der
Konstruktor Pow()
tatsächlich etwas: Da base_
und exp_
Null-Initialisiert wurden, muss result_
auf 1
gesetzt
werden, denn 0\^0=1.
Okt 08, 2013
Soso, da gibt es also ein neues Buch vom
Erfinder: A Tour of C++,
Bjarne Stroustrup.
Ich habe viele Stimmen gehört, die sein Standardwerk zum C++-Standard gut fanden, aber vielleicht war ich damals einfach
zu früh.
Ein schneller Blick in sein neues – viel schlankeres – Werk ist nach meinem Geschmack: Viel Überblick, einige Details.
Ein wenig vermisse ich die Zusammenhänge und wie die Sprachelemente vernetzt verwendet werden, aber wie gesagt, es war
bisher nur ein schneller Blick, ich werde das prüfen. Ich denke zu dem Preis und der “Dünne” macht man nichts falsch.
Okt 07, 2013
Manuelles Swap
Elemente zu vertauschen ist an vielen Stellen eine gefährliche Operation. Zu Zeiten ohne Exceptions und ohne *
Multithreading* war vertauschen noch einfach:
Typ temp = a;
a = b;
b = temp;
Aber was passiert hier genau:
Typ temp = a;
– erzeugt eine neue Instanz und initialisiert sie mit a
, per Copy ConstructorTyp(const Typ&)
.
a = b;
– ruft den Zuweisungsoperator Typ& operator=(const Typ&)
auf
b = temp;
– ruft noch einmal den Zuweisungsoperator Typ& operator=(const Typ&)
auf.
- Weil es eine neue Instanz ist, wird
temp
später per Destruktor weggeräumt.
Problempunkte
Bei diesem Prozedere gibt es vor allem zwei Dinge zu beachten:
- Die Ressourcen, die
Typ
hält, werden diese wegen des Copy C’tors einmal kopiert.
- Schade, wenn dies zum Beispiel ein riesengroßer Speicherblock ist, zu dem vielleicht nur ein Pointer getauscht werden müsste
- Manche Resourcen können nicht kopiert werden: Locks, Mutexe, Filehandles machen Probleme, etc. Dann funktioniert das ganze Vorgehen nicht.
- Was, wenn
b = temp
eine Exception wirft? Der Zuweisungsoperator sollte das nicht, aber wenn man dessen Code nicht unter Kontrolle hat? Dann sind a
und b
nicht vertauscht, sondern identisch. Das alte a
ist verloren.
Der bessere Weg
Daher wird seit jeher die Verwendung der swap
-Funktion aus der Standardbibliothek empfohlen:
using std::swap;
swap(a, b);
oder seit C++11 auch einfach
denn die Namensauflösung der swap
-Implementierung wurde leicht geändert (Details im Buch ;-) oder
bei Stackoverflow).
Edit: Es ist ein Idiom, die swap
-Funktion eine friend
-Memberfunktion zu machen, auch dazu mehr
bei Stackoverflow.
Auch swap
soll – wie der Zuweisungsoperator (und andere Beteiligte) – keine Exceptions werfen. Und da für die swap
-Implementierung empfohlen wird, die Member elementweise wieder mit swap
zu vertauschen, würde statt der Kopie der
Ressourcen * im Falle des großen Datenblocks nur der Pointer ge-swapped * im Falle der nicht-kopierbaren Ressource
diese vertauscht, was gehen sollte.
Daher merke (seit jeher): Eigene Datentypen sollten swap
implementieren.
Neuer Operator :=:
Um die Wichtigkeit von swap
zu unterstreichen, gibt es
den Vorschlag,
swap
als Operator :=:
einzuführen. Das sähe dann so aus:
struct Typ {
Data a;
Feld b;
};
Typ& operator:=:(Typ& lhs, Typ& rhs) noexcept {
lhs.a :=: std::move(rhs.a);
lhs.b :=: std::move(rhs.b);
return lhs;
}
Typ& operator:=:(Typ& lhs, Typ&& rhs) noexcept {
return rhs;
}
Statt nacheinander die Elemente mit swap
zu vertauschen verwendet man den neuen :=:
-Operator. Beziehungsweise
die &&
-Variante, die für das swappen mit der rhs
-Instanz da ist, die ohnehin bald weggeworfen würde gestaltet sich
die Implementierung besonders leicht: Nimm als lhs
einfach rhs
.
Dadurch ist hervorgehoben, dass es sich um eine sehr spezielle Funktion handelt, und kommt somit namenstechnisch
dem operator=
näher. Der Benutzer wird eher darauf gestoßen, dass er sie implementieren sollte.
Es gibt nich weitere
kleine Ergänzungen
für :=:
, zum Beispiel für die eingebauten Typen.
Glaskugel
Hauptsächlich handelt es sich um eine Sytnax-Erweiterung mit nur wenig funktionalen Ergänzungen. Meine Vorraussage ist,
dass dieses Feature es nicht in den Standard schafft. Wenn es nur um Syntax geht, war das Kommitee eher zurückhaltend.
Sep 09, 2013
Mal was ganz anderes. Wöchentlich bekomme ich von Linkedin Mails, wie folgt:
Torsten,
Congratulations! Your connection Xyz Abc has endorsed you for the following skills and expertise:
Perl
Zugegeben, ich habe mein Profil vor ca 11 Jahren dort erstellt, als ich aus der Bioinformatik (Perl-lastig) heraus einen
neuen Job suchte, aber wir wissen ja: Das Internet vergisst nie.
Ja, Perl habe ich mal gemacht, ja ich kann Perl-Programme lesen und zur Not auch erweitern, ja, darum steht da *
Perl*. Nicht dass ich da besonders storlz drauf bin, auch egal ob ich jemals wieder Perl anfassen möchte oder nicht –
es steht da.
Schade nur, dass von meinen Kollegen damals niemand wusste, dass ich seit jeher viel lieber (und besser) C++ mache, aber
wahrscheinlich mehr “hinter den Kulissen”. Und die noch älteren Kollegen wissen nicht, dass ich vor 12 Jahren zu Python
gewechselt bin – in gewissen Kreisen scheinbar nicht so verbreitet, seltsamerweise ;-).
Sagen wir ich habe o.B.d.A. 100 nette Kollegen und Ex-Kollegen.... dann kann ich jetzt zu 90% Perl, zu 50%
Python und zu 5% diese extrem esoterischen Sprache… uh, wie heißt die noch? Achja… C++.
Hach, das Internet funktioniert einfach nicht.
/schmunzel
/rant.
Sep 04, 2013
Was für ein Überseher! Da gibt es die wichtige und nützliche
Funktion make_shared()
um einen shared_ptr
ssicher zu erzeugen, und in dem ganzen Trubel um die Fertigstellung von
C++11 wurde das Pendant make_unique()
übersehen. Das lässt sich zum Glück leicht korrigieren und die meisten
C++11-konformen Compiler sollten mit dieser Bibliothekskorrektur mitgeliefert werden. In C++14, was auf dem besten Weg
ist, ist es dann enthalten.
Aug 16, 2012
Der GCC hat nun
eine C++-Implementierung für
alle drei Compilerphasen.
Da lohnt es sich mal einen Blick darauf zu werfen, was Gnu von C++ alles nutzen möchte. C++ ist nunmal eine große
Sprache und man muss sich in einem Projekt entscheiden, was man davon nutzen will.
Ich fasse mal ganz knapp die Wiki-Seite zusammen. Diese Konventionen haben
aber noch recht wenig mit C++11 zu tun, weil “der aktuelle GCC sich immer mit der vorigen (stabilen) Version bauen
lassen muss”.
Allgemeine und Dialekt-Regeln
- Der Code muss sowohl C++03 als auch C++11 konform sein.
Das liegt natürlich daran, dass eine vorige Version den neuen Compiler übersetzen können muss. In einem eigenen
Projekt müsste man diese Regel nicht unbedingt verwenden.
- Kein RTTI und somit kein
dynamic_cast
und keine dynamischen Exceptionsspezifikationen throw(...)
- und (tadaaa:) keine Exceptions! Warum, das wird auch erwähnt: Im Moment ist der Großteil des GCC-Codes nicht
Exception-Safe. Dass es gut wäre, den Code auf RAII umzustellen, wird aber auch erwähnt. Das ist jedoch extrem viel
Aufwand. Dann wären Exceptions natürlich nötig.
- Statt
auto_ptr
nur tr1::shared_ptr
, denn ersterer sei defekt. Wenn man sich selbst für C++11 entscheidet, dann
kann man wohl auch den C++11 shared_ptr
verwenden.
Welche Features?
- Keine neuen Templates einführen.
- Keine
usings
und keine Nested Namespaces.
- Statt Konversions-Operatoren immer explizite Methoden verwenden. Also statt
operator int()
besser int getInt()
.
- Konstruktoren mit einem Argument sollten immer
explicit
sein. Auch dies vermeidet überraschende Konvertierungen.
- Keine Mehrfachvererbung.
- Copy- und Assignment-Operatoren sollten vermieden werden, sind aber o.k. für pure Werteklassen. Der Grund ist,
dass der Compiler selbst brauchbare Versionen generiert, wenn die Klasse keine Pointer enthält. Und wenn sie das tut,
dann soll man sicherheitshalber Copy und Assign verbieten. Das führt langfristig dazu, dass Entwickler weniger
Raw-Pointer verwenden werden.
C++ Konventionen
class
und struct
sind konzeptionell zu unterscheiden.
- Konstruktoren sollen alle Datenfelder in ihrer Initialisierungsliste setzen.
- Alle Datenfelder von Klassen sind
private
.
- Namen von Datenfeldern sollten mit einem “
_
” enden, also zum Beispiel data_
.
- Methoden sollten diese Datefelder dann mit
this
verwenden, also zum Beispiel this->data_
(oder natürlich dem Getter).
Das kann als Anregung für eigene Konventionen dienen.
Mai 29, 2012
Natürlich wird es nicht ein paar Jahre dauern bis aus der Ankündigung, dass eine Arbeitsgruppe die Arbeit aufnimmt letztlich ein Standard wird, aber es geht vorwärts.
Im Oktober will die Study Group 4 Ideen zur Standardisierung zur Netzwerkprogrammierung vorstellen: IPv4 und IPv6-Adressen, Adressen auflösen, Ports und URIs, Datagramm- und Netzwerk-Streams sowie ein erstes SSL-Interface. Auf dieser Basis sollen dann später höhere Protokolle wie HTTP und FTP aufgesetzt werden.
Bis dahin bittet die Gruppe um die Einreichung von Vorschlägen für ein Socket Interface, das zum Standard werden könnte.
Mai 02, 2012
Scott Meyers hat erste Ideen zu seinen
Empfehlungen für ein potentielles Effective C++11.
Einige Kandidaten findet man indirekt auch in C++11 programmieren
- “Prefer auto to Explicit Type Declarations” – die Vorteile werden in
[typeinfer.auto]
besonders beschrieben, aber auch an vielen anderen Stellen
- “Prefer non-member begin/end to member versions” – Ausführlich in
[syntax.for]
besprochen
- “Be Wary of Default Capture Modes in Lambdas Escaping Member Functions” – Thema in
[lambda.args]
- “Pass std::launch::async if Asynchronicity is Essential” – wird in
[thread.async]
erwähnt
- “Distinguish Rvalue References from Universal References” – Der Unterschied zwischen
&
und &&
wird in [rval.intro]
besprochen.
- “Prefer Lambdas over Binders” – die Vorteile stehen auch in
[func.funcobjs]
- “Apply std::forward when Passing Universal References” – ausführlich in
[rval.forward]
erklärt
- “Prefer std::array to Built-in Arrays” – wird in
[stl.algorithm]
angerissen
- “Use std::make_shared Whenever Possible” in
[stl.shptr]
begründet
- “Reserve noexcept for Functions with Wide Interfaces” – auch empfohlen in
[std.noexcept]
- “For Copyable Types, View Move as an Optimization of Copy” – angerissen in
[class.delete]
- “Prefer enum classes to enums” – begründet mit Beispielen in
[enum.typed]
- “Prefer nullptr to NULL and 0 – exakt wie in
syntax.nullptr
beschrieben
Ich freue mich jedoch darauf, wieder eine exzellente Zusammenfassung und Begründung von Scott Meyers in den Händen zu halten!
Apr 03, 2012
…Das etwas andere Lehrbuch (Galileo Computing)
Ich kann dieses Buch sehr empfehlen.
Es liefert einen guten Einstieg in C++.
Es ist locker geschrieben, bleibt aber dennoch bei den Fakten. Es bringt die C++igen Konzepte durchaus herüber, ist
jedoch nicht dogmatisch. Der Leser wird auch nützliche C’ismen erlernen, aber meist mit dem Hinweis, dass es sich
hierbei um “gutes altes Tse” handelt. Am anderen Ende der Skala kommt auch C++11 nicht zu kurz.
Das Buch ist auch noch aufwendig farbig produziert, ein Augenschmaus und Schmuck auf dem Schreibtisch.
Externe Links
Apr 03, 2012
Mit der Verabschiedung von C++11 gibt sich die Gemeinde natürlich nicht zufrieden. Auch C++ wird sich natürlich noch
weiter entwickeln. Einige Dinge wurden aus C++0x herausgelassen, damit “0x” es letztendlich wenigstens “11” werden
konnte. Man wird aber sicher alte Bekannte irgendwann wiedersehen.
Dazu gehören zuvorderst natürlich
die Concepts, aber viele erwarten
auch Asynchrone IO
oder Filesystem- und Dateinamenbehandlung
– oder irgendetwas aus dem Umfeld von Boost.
C++1y, TR, oder Papierkorb
Was in den Standard Einzug halten wird, ob wann, oder ob es einen Nachtrag oder Technical Report mit Erweiterungen geben
wird, steht natürlich in den Sternen. Nein, nicht in den Sternen – man kann sich an den Diskussionen ja beteiligen und
vielleicht deren Ausgang ein wenig beeinflussen. Für den Interessierten lohnt es sich also, die Augen offen zu halten…
Es tut sich viel, aber ich greife mal exemplarisch etwas heraus…
integer und unsigned_integer
Neu in der Diskussion ist eine kleine aber feine Erweiterung für Ganzzahlen mit unbegrenzter Genauigkeit. “Unbegrenzt”
meint hier dynamisch unbegrenzt, im Vergleich zu statisch unbegrenzt. Es wäre ein wenig leichter, zum Beispiel einen
Zahltypen zu definieren, bei dem man bei der Initialisierung eine maximale Länge angeben muss – überschreitet man die,
gäbe es einen Überlauf. Statt aber die Klasse integer
auf einer Art festem Array zu basieren, schwebt den Autoren
von N3375 konzeptionell eher ein vector
vor. So kann kein Überlauf mehr passieren, die Größe passt sich automatisch an – bis der Speicher ausgeht…
Da gibt es natürlich schon jede Menge Bibliotheken (zum Beispiel die vielseitige Gnu GMP)
, aber die wenigsten schmecken nach C++11.
Mit den bestehenden Zahlentypen wird integer
natürlich interagieren. Die arithmetischen wie +
, *
und Verwandte
werden mit sich selbst und int
-Varianten unterstützt.
integer i = 30000;
integer j = 1000 * i;
Allerdings geschieht dies im aktuellen Vorschlag nicht durch Überladung von operator+()
etc mit allem möglichen int
-Varianten, sondern durch die implizite Umwandlung von int
in integer
.
Der aktuelle Vorschlag bietet:
- arithmetische Operationen +, -, *, /, %, – und ++
- eine Funktion div(), die gleichzeitig das ganzzahlige Ergebnis und den Rest zurückgibt,
- Bitoperationen |, &, \^, \<\<, >>, \<\<=, >>=, |=, &= und\^=.
- Vergleiche ==, \<, >, !=, \<= und >=
- Quadrat, Quadratwurzel sowie `
sqrtrem()
` um gleichzeitig Quadratwurzel und Rest zu erhalten
- Exponenzieren mit
pow()
, bzw. powmod()
– letzterest ist ein häufiger Anwendungsfall, der eine
platzsparendes pow()
gefolt von einer Modulo-Operation ist. Den GGT und KGV kann man mit gcd()
und lcm()
berechnen.
- Eine beliebig lange Zufallszahl kann man erstellen,
- Stream-Ein- und Ausgabe wird unterstützt; mit `to_string(base=10)` ist ebenfalls praktisch.
Noch Offen
Chancen sich einzubringen hat man vielleicht insbesondere bei den noch offenen Punkten des aktuellen Vorschlags:
- Wie soll die Speicherverwaltung duch den Benutzer beeinflußt werden können? Wie sollen Allocators Eingang
finden?
- Was soll passieren bei einem Unterlauf, zum Beispiel bei
unsigned_integer neg = -99;
?
unsigned_integer five = 5
und dann unsigned_integer result = five - 100
?
unsigned_integer five = 5; unsigned_integer ten = 10
, dann five - ten
?
- Wie ist mit Präzisionsverlust umzugehen, wenn ein sehr großer
integer
in einen int
umgewandelt werden soll?
Wer hat Vorschläge?
Links
N3375 - Proposal for Unbounded-Precision Integer Types