Modernes C++ programmieren

Nov 18, 2014

Auto und for-Schleifen

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

FAQ001: modern102.cpp compiliert nicht auf Microsoft Visual Studion 2012

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.

Mai 29, 2012

C++ Networking als Teil des Standards

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.

posted at 15:01  ·   ·  C++  c++11  ft  http  ipv4  ipv6  networking  sg4

Mai 02, 2012

Effective C++11 -- Scotts erste Ideen

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!