Open Source im professionellen Einsatz
Linux-Magazin 04/2014

Modernes C++ in der Praxis – Folge 15

C++11 + 3 = C++14

2014 ist ein besonderes Jahr für C++. Drei Jahre nach C++11 erfährt der Sprachstandard mit C++14 den letzten Feinschliff. Neben generischen Lambda-Funktionen und der vereinfachten Ermittlung des Rückgabetyps kann C++14 vor allem mit einem Feature punkten: Reader-Writer-Locks.

1190

Zugegeben, C++ ist nicht mehr das, was es einmal war. Mit C++11, C++14 und C++17 muss sich die Programmiersprache immer wieder mit Veränderungen auseinandersetzen. Der Zeitstrahl zu den Standards in Abbildung 1 soll das Zahlenspiel entwirren: Während C++98, C++11 und C++17 (oben) vollwertige Standards für die Sprache sind, stellen C++03 und C++14 (unten) Überarbeitungen der zu diesem Zeitpunkt gültigen Norm dar. Sie beseitigen im Wesentlichen Bugs und bringen kleine Verbesserungen.

Abbildung 1: Die C++-Standards: Oben die maßgeblichen Neuauflagen, unten die Nachbesserungen der Sprachversionen.

Auch wenn C++17 suggeriert, dass genau im Jahr 2017 der nächste große Wurf ansteht, so ist das keine zuverlässige Datumsangabe, sondern eine Arbeitshypothese. Einen Einblick in den laufenden Standardisierungsprozess gibt [1].

Redundanz beseitigt

Gerade mal zwei Monate ist es her, da stellte ein Artikel [2] dieser Serie die automatische Ermittlung des Rückgabetyps eines Funktionstemplates vor. Bereits mit C++14 (Listing 1) wird das einfacher. Das Funktionstemplate »add« in Zeile 4 ermittelt den Rückgabetyp automatisch aus dem Ergebnis des Ausdrucks »decltype(fir + sec)« in Zeile 5. Dabei leitet das Schlüsselwort »auto« den verzögerten Rückgabetyp ein. Ein scharfer Blick auf das Funktionstemplate offenbart die Redundanz: Der Ausdruck »fir + sec« muss in C++11 sowohl bei der Definition des Rückgabetyps (Zeile 5) als auch bei der Berechnung des Rückgabewerts (Zeile 6) stehen.

Listing 1

Vereinfachte automatische Rückgabetypen

01 #include <iostream>
02 #include <typeinfo>
03
04 template <typename T1, typename T2>
05 auto add(T1 fir, T2 sec) -> decltype(fir + sec){
06   return fir + sec;
07 }
08
09 template <typename T1, typename T2>
10 auto add14(T1 fir, T2 sec){
11   return fir + sec;
12 }
13
14 int main(){
15
16   std::cout << std::endl;
17
18   auto a= add(2000,11);
19   auto b= add(2000L,11);
20   auto c= add(3,0.1415);
21
22   std::cout << "a: " << a <<  " of type " << typeid(a).name() << std::endl;
23   std::cout << "b: " << b <<  " of type " << typeid(b).name() << std::endl;
24   std::cout << "c: " << c <<  " of type " << typeid(c).name() << std::endl;
25
26   std::cout << std::endl;
27
28   auto a1= add14(2000,14);
29   auto b1= add14(2000L,14);
30   auto c1= add14(3,0.1415);
31
32   std::cout << "a1: " << a1 <<  " of type " << typeid(a1).name() << std::endl;
33   std::cout << "b1: " << b1 <<  " of type " << typeid(b1).name() << std::endl;
34   std::cout << "c1: " << c1 <<  " of type " << typeid(c1).name() << std::endl;
35
36   std::cout << std::endl;
37
38 }

Das ist in C++14 beim Funktionstemplate »add14« (Zeile 9) nicht mehr erforderlich. Die Sprache ermittelt nun automatisch den Rückgabetyp aus dem Rückgabewert. Wie das Ausführen des Programms in Abbildung  2 zeigt, sind die mit unterschiedlicher Notation automatisch ermittelten Rückgabetypen identisch. So ergibt »int + int« den Typ »int« (Zeilen 22 und 28), »long + int« den Typ »long« (Zeilen 19 und 29) und »double + int« den Typ »double« (Zeilen 20 und 30).

Abbildung 2: Automatisches Bestimmen des Rückgabetyps mit C++11 und C++14.

Generische Lambda-Funktionen

Lambda-Funktionen sind eines der wichtigsten Features von C++11. Mit ihnen drückt der Programmierer Funktionalität kurz und genau an der Stelle aus, an der er sie benötigt. Bessere Lesbarkeit und höhere Flexibilität des Codes sind zwei der vielen Punkte, die für Lambda-Funktionen sprechen. Die Details zu diesem funktionalen Baustein in C++11 lassen sich in den zwei ersten Artikeln dieser Serie nachlesen ([3], [4]).

Was fehlt den Lambda-Funktionen in C++11, das C++14 bietet? In C++11 muss der Entwickler den Typ der Argumente explizit angeben – das verhindert ihren generischen Einsatz. Den feinen Unterschied zwischen Lambda-Funktionen und generischen Lambda-Funktionen machen die zwei Funktionen »add()« und »add14()« in den Zeilen 7 und 8 von Listing 2 anschaulich. Während die erste nur Argumente vom Typ »int« akzeptiert, nimmt die zweite Argumente beliebigen Typs an. Kleiner Unterschied, große Auswirkungen: In Zeile 14 lassen sich mit »add()« lediglich zwei »int« -Werte zusammenzählen. Im Gegensatz dazu addiert »add14()« in den Zeilen 16 bis 22 Werte der Typen »int« , »long« , »double« und »std::string« .

Compiler für C++14

Die Version 3.4 des Clang-Compilers [5] unterstützt als erste vollständig den C++14-Standard. Aber auch der aktuelle GCC 4.9 [6] bietet die meisten Features von C++14. Mit »std=c++1y« als Flag übersetzt er alle Beispiele dieses Artikels. Genaueres zur Compiler-Unterstützung von C++14 ist im neuen C++-FAQ-Wiki [7] nachzulesen.

Listing 2

Generische Lambda-Funktionen

01 #include <algorithm>
02 #include <iostream>
03 #include <string>
04
05 using namespace std::literals;
06
07 auto add=[](int i,int i2){ return i + i2; };
08 auto add14=[](auto i,auto i2){ return i + i2; };
09
10 int main(){
11
12   std::cout << std::endl;
13
14   std::cout << "add(2000,11): " << add(2000,11) << std::endl;
15
16   std::cout << "add14(2000,14): " << add14(2000,14) << std::endl;
17   std::cout << "add14(2000L,14): " << add14(2000L,14) << std::endl;
18   std::cout << "add14(3,0.1415): " << add14(3,0.1415) << std::endl;
19
20   std::cout << "add14(std::string(\"Hello\"),std::string(\" World\")): "
21             << add14(std::string("Hello "),std::string("World")) << std::endl;
22   std::cout << "add14(\"Hello \"s,\"World\"s): "  << add14("Hello "s,"World"s) << std::endl;
23
24   std::cout << std::endl;
25
26   std::vector<int> myVec{1,2,3,4,5,6,7,8,9};
27   auto res= std::accumulate(myVec.begin(),myVec.end(),0,[](int i,int j){ return i+j;});
28   std::cout << "res: " << res << std::endl;
29
30   auto res14= std::accumulate(myVec.begin(),myVec.end(),0, add14);
31   std::cout << "res14: " << res14 << std::endl;
32
33   std::vector<std::string> myStr{"Hello"s," World"s};
34   auto str14= std::accumulate(myStr.begin(),myStr.end(),""s, add14);
35   std::cout << "str14: " << str14 << std::endl;
36
37   std::cout << std::endl;
38
39 }

Besonders praktisch sind generische Lambda-Funktionen beim Einsatz in der Standard Template Library. Sie machen es möglich, Algorithmen ohne die Angabe eines Typs zu formulieren. Der subtile Unterschied zwischen »std::accumulate« in Zeile 27 und »std::accumulate« in den Zeilen 30 und 34 ist, dass die generische Lambda-Funktion »add14()« sowohl für Vektoren vom Typ »int« als auch solche vom Typ »std::string« geeignet ist. Diese Flexibilität fehlt der Lambda-Funktion »[](int i, int j){ return i+j; }« in Zeile 27. Sie lässt sich nur auf »int« -Typen anwenden. In Abbildung 3 ist das Programm in Aktion zu sehen.

Abbildung 3: Lambda-Funktionen versus generische Lambda-Funktionen.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 5 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • C++11

    Die neue Zeitbibliothek ist ein elementarer Baustein nicht nur für die Mulithreading-Fähigkeit von C++. Mit ihrer Hilfe legt der Entwickler einen Thread bis zu einem definierten Zeitpunkt schlafen oder fordert auf gut Glück ein Lock für eine Zeitspanne an.

  • C++11

    Die neue Zeitbibliothek von C++11 erweist sich als elementarer Bestandteil der Threading-Schnittstelle: Sowohl Threads, Locks und Bedingungsvariablen als auch Futures haben ein Verständnis von Zeit. Dank ihrer Unterstützung kann ein Entwickler unterschiedliche Wartestrategien verfolgen.

  • C++11

    Die Referenz-Wrapper bilden eine kleine, aber feine neue Bibliothek in C++11. Diese Objekte verhalten sich wie Referenzen, der Artikel erklärt die Details.

  • C++11

    In C++11 lässt sich manches prägnanter formulieren als in klassischem C++. Dieser Artikel zeigt, wie die neue Range-basierte For-Schleife und die automatische Typableitung dabei helfen.

  • Reichhaltiges Angebot

    C++0x bringt nicht nur Veränderungen in der Kernsprache. Die Standardbibliothek der C++-Neuausgabe hat Multithreading, asynchrone Funktionsaufrufe, reguläre Ausdrücke und vieles mehr im Angebot.

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.