C++0x
Surprisingly, C++0x feels like a new language: The pieces just fit together betterC++0x bricht nicht mit C++ Code.
than they used to and I find a higher-level style of programming more natural than before
and as efficient as ever. (Bjarne Stroustrup)
Historie
- Zeitachse C++:
- ARM C++
- Annotated C++ Reference Manual (ARM)
- erster C++ Standard
- Grundlage für C++98
- Bjarne Stroustrup definiert C++ Funktionsumfang
- C++98 (ISO/IEC 14882)
- aktuelle ISO C++ Standard
- C++03
- technische Korrektur des Standards
- TR1
- Technical Report 1
- Ergänzungen der C++ Standard Library
Ziele von C++0x
DenPrinzipien von C++
- C++ ist eine Multi-Paradigmen Programmiersprache.
- Vertraut dem Programmierer,
- Zahle nicht für das, was du nicht benutzt.
- Brich keinen funktionierenden Code.
- Kontrolle zur Kompilezeit ist besser als zu Laufzeit.
Prinzipien von C++0x
- Bessere Systemprogrammierung ermöglichen.
- Bessere Sprache für die Implementierung von Bibliotheken.
- Soll leichter zu lehren und zu lernen sein.
Kernsprache
Mächtigere Klassendefinition
Ein paar neue Konstrukte:class NonCopyableClass{
NonCopyableClass & operator=(const NonCopyableClass&) = delete;
NonCopyableClass(const NonCopyableClass&) = delete;
NonCopyableClass() = default;
};
class BaseClass{
virtual void foo();
void bar();
};
class DerivedClass [[base_check]]: public BaseClass{
void foo [[override]] ();
void foo [[override]](int)
void bar [[hiding]] ();
void bar [[hiding]](int);
};
class DelegateConstructor {
int value;
public:
DelegateConstructor(int v) : value(v) {}
DelegateConstructor() : DelegateConstructor(17) { }
};
class BaseClass{
public:
BaseClass(int i);
};
class DerivedClass : public BaseClass{
public:
using BaseClass::BaseClass;
};
template<typename T> class MyVector {
public:
MyVector(std::initializer_list<T> list); // initializer-list constructor
MyVector(const MyVector&); // copy constructor
MyVector(MyVector&&); // move constructor
MyVector& operator=(const MyVector&); // copy assignment
MyVector& operator=(MyVector&&); // move assignment
};
defaulted und deleted Standardmethoden
- die Klasse NonCopyableClass ist nicht kopierbar
- delete: unterdrückt sowohl den automatisch erzeugten Kopier- als auch den Zuweisungsoperator
- default: ein Defaultkonstruktor wird erzeugt, für dessen Implementierung der Kompiler sorgt
- implementiert der Anwender in einer Klasse ein Konstruktor, wird in C++ der Defaultkonstruktor nicht erzeugt der Anwendert muß diesen selber schreiben
void *operator new(std::size_t) = delete;
führt in einer Klasse dazu, das diese nicht mehr dynamisch mittels new erzeugt werden kann
override und hiding von Methoden
- durch das Attribut base_check der Klasse DerivedClass muß das Überschreiben oder Verstecken von Basisklassenmethoden explizit ausgedrückt werden
- override: diese Methode will eine Methode überschreiben
- hidding: diese Methode will eine Methode verstecken (
void bar()
versteckt die gleichnamige Methode der Basisklasse) - sowohl
void foo [[override]](int)
als auchvoid bar [[hidding]](int)
sind Syntaxfehler
Delegation von Konstruktoren
- gemeinsame Funktionalität aller Konstruktoren wurde in C++ in einer
init()
Methode angeboten, die alle Konstruktoren aufzurufen hatten - die Konstruktoren der Klasse DelegateConstructor rufen explizit oder implizit den Konstruktor
DelegateConstructor(int v)
auf, der darfür sorgt, das value auf 17 initialisiert wird
Vererbung von Konstruktoren
using BaseClass::BaseClass
: alle Konstruktoren der Basisklasse in der abgeleiteten Klasse stehen zur Verfügung
Initialiser List Konstruktor
MyVector(std::initializer_list list)
ist ein neuer Konstruktor, der über eine Sequenz von homogenen Wert initialisiert werden kann:MyVector myIntVec={1,2,3,4,5}
- jeder Standardcontainer (vector, list, deque, map, multimap, set, multiset) besitzen diesen Konstruktor, so dass sich diese direkt mit Aggregaten befüllen lassen
- eigene Datentypen oder Funktionen können auch direkt mittels der Sequenz
(std::initializer_list list)
mit Aggregaten umgehen:
double sumAll(std::initializer_list list)
Move Semantic
- Ziel der Move Semantic ist es unnötiges Kopieren zu vermeiden
MyVector(MyVector&&);
undMyVector& operator=(MyVector&&)
werden move constructor und move assignment genannt- syntaktisch unterscheiden sich diese vom copy constructor und copy assignment durch zwei && (rvalue Referenzen)
- sie statten die Daten mit einer Move Semantic aus, den beim Erzeugen von neuen aus alten Objekten werden die Inhalte transferiert und nicht kopiert
- Beispiel: Copy Semantic versus Move Semantic
std::cout << "move semantic for vector: " << std::endl;
std::vector <int> vec1={1,2,3,4,5,6,7,8,9};
std::vector <int> vec2;
std::cout << "vec1.size(): " << vec1.size() << " vec2.size(): " << vec2.size() << std::endl;
vec2= std::move(vec1);
std::cout << "vec1.size(): " << vec1.size() << " vec2.size(): " << vec2.size() << "\n\n";
std::cout <<"copy semantiv for vector: " << std::endl;
std::vector <int> vec3;
std::cout << "vec2.size(): " << vec2.size() << " vec3.size(): " << vec3.size() << std::endl;
vec3= vec2;
std::cout << "vec2.size(): " << vec2.size() << " vec3.size(): " << vec3.size() << std::endl;
- der Codeschnipsel erzeugt folgende Ausgabe
move semantic for vector:
vec1.size(): 9 vec2.size(): 0
vec1.size(): 0 vec2.size(): 9
copy semantiv for vector:
vec2.size(): 9 vec3.size(): 0
vec2.size(): 9 vec3.size(): 9 - das ganze visualisiert
- Move Semantik:
- Copy Semantik:
- Move Semantik:
Die Move Semantic stellt bei Container ein nicht zu unterschätzendes Optimierungspotential dar. Sowohl die Initializer List als auch die Move Semantic Konstruktoren bietet jeder Standardcontainer in C++0x an.
Einfacheres Arbeiten mit der Standard Library
Der hochaktuelle gcc 4.5 kann bis auf die range basierte for loopfor (auto itr : myInt) { std::cout << *itr << " ";}
das folgende Codeschnipsel übersetzten std::vector <int> myInt(10);
std::cout << "vector of 10 times 0: ";
for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
std::cout << *itr << " ";
}
std::cout << "\n\n";
// for (auto itr : myInt) { std::cout << *itr << " ";}
// for (std::vector<int>::const_iterator itr= intInt.begin(); itr != myInt.end(); ++itr){ ... }
std::iota(myInt.begin(), myInt.end(),1);
std::cout << "Increment each value by 1: ";
for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
std::cout << *itr << " ";
}
std::cout << "\n\n";
std::sort(myInt.begin(),myInt.end(),[](int a, int b){ return a >= b;} );
std::cout << "Sort the vector of natural numbers: ";
for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
std::cout << *itr << " ";
}
std::cout << "\n\n";
std::cout<< "std::min({1,2,4,0,1,-5,2}): " << std::min({1,2,4,0,1,-5,2}) << "\n\n";
std::map<std::string,std::string> phonebook =
{ {"Bjarne","+1 (012) 555-1212"},
{"Herb", "+1 (345) 555-3434"},
{"Alexandrei","+1 (678) 555-5656"} };
std::cout << "Get each telephon number: \n";
for (auto itr = phonebook.begin(); itr!= phonebook.end(); ++itr){
std::cout << itr->first << ": " << itr->second << "\n;
}
- und die Ausgabe
vector of 10 times 0: 0 0 0 0 0 0 0 0 0 0
Increment each value by 1: 1 2 3 4 5 6 7 8 9 10
Sort the vector of natural numbers: 10 9 8 7 6 5 4 3 2 1
std::min({1,2,4,0,1,-5,2}): -5
Get each telephon number:
Alexandrei: +1 (678) 555-5656
Bjarne: +1 (012) 555-1212
Herb: +1 (345) 555-3434
Initialiser Listen
- {}-Initialiser bieten einheitliche Initialisierung von Objekten an und können für alle Initialisierungen verwendet werden
- Beispiele:
- Funktionsaufruf:
std::min({1,2,4,0,1,-5,2})
- std::map:
std::map<std::string,std::string> phonebook = { {"Bjarne","+1 (012) 555-1212"} }
- std::vector:
std::vector vec1={1,2,3,4,5,6,7,8,9}
- Funktionsaufruf:
Base x = Base{1,2};
Base* p = new Base{1,2};
struct Derived : Base {
Derived(int x, int y) :Base{x,y} {};
};
func({a}); // function invocation
return {a}; // function return value
Neue Algorithmen
std::iota(myInt.begin(), myInt.end(),1)
inkrementiert die Elemente des Vektors sukzessive um 1std::min({1,2,4,0,1,-5,2},[](int a,int b){ return abs(a) >= abs(b)})
kann eine Vergleichsfunktion übergeben werden- neben Funktionspointern und Funktionsobjekten (Funktor) kann die Vergleichsfunktion direkt definiert werden (Lambda Funktion)
- die restlichen neuen Algorithmen: http://www2.research.att.com/~bs/C++0xFAQ.html#algorithms
Lambda Funktionen
- Aufbau:
[Bindung](Argumente){Funktionskörper}
[]
: Bindung an den lokalen Scope (Lambda Funktionen sind Closures)[]
: keine Bindung[=]
: Werte werden kopiert[&]
: Werte werden referenziert
()
: Argumente (optional) der Lambda Funktion{}
: Funktionskörper,- ein einzelner Ausdruck ist gleichzeitig der Returnwert
- kann auch Anweisungen enthalten return Anweisung notwendig
- die Details können unter http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=25 nachgelesen werden
- Beispiele:
std::map< const char , std::tr1::function<double(double,double)> > dispTable;
dispTable.insert( std::make_pair('+',[](double a, double b){ return a + b;}));
dispTable.insert( std::make_pair('-',[](double a, double b){ return a - b;}));
dispTable.insert( std::make_pair('*',[](double a, double b){ return a * b;}));
dispTable.insert( std::make_pair('/',[](double a, double b){ return a / b;}));
std::cout << "3+4= " << dispTable['+'](3,4) << std::endl;
std::cout << "3-4= " << dispTable['-'](3,4) << std::endl;
std::cout << "3*4= " << dispTable['*'](3,4) << std::endl;
std::cout << "3/4= " << dispTable['/'](3,4) << std::endl;
[]{
std::cout << "inline lambda";
std::cout << " function.\n";
}();
auto myAdd= [](int a, int b){return a + b;};
std::cout << "myAdd(1,2): " << myAdd(1,2) << std::endl;
- dispTable ist ein dispatch Table mit Hilfe von Lambda Funktionen und den neuen Wrapper für Funktionsobjekte std::tr1
- die namenslose Funktion wird in-place augerufen
- myAdd bindet die lambda Funktion, dessen Typ durch
auto
bestimmt wird - Ausgabe:
3+4= 7
3-4= -1
3*4= 12
3/4= 0.75
inline lambda function.
myAdd(1,2): 3
Type Inference
auto
auto
erlaubt es, den Typ aus einem Initialiser abzuleiten- damit können Schleifen deutlich kompaker geschrieben werden
std::vector <int> myInt{1,2,3,4,5};
for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
std::cout << *itr << " ";
}
for (auto itr : myInt) {
std::cout << *itr << " ";
}
decltype
decltype
erlaubt es, den Typ aus einem Ausdruck abzuleiten- damit lässt sich der Typ von komplexen Berechnungen ohne Template Metaprogramming Magic bestimmen
std::vector <int > myVecInt={1};
std::vector <double> myDoubleInt={1.1};
decltype(myVecInt[0]*myDoubleInt[0]) myDouble = 2.1;
void foo(const vector<int>& a, vector<float>& b){
typedef decltype(a[0]*b[0]) ErgTyp;
for (int i=0; i<b.size(); ++i) {
ErgTyp* p = new ErgTyp(a[i]*b[i]);
...
- myDouble besitzt letztendlich den Typ, den es verspricht
typedef decltype(a[0]*b[0]) ErgTyp
erklärt einen neuen Typ, der sich aus der Multiplikation ergibt float
Generische Programmierung
Variadic Templates
- Templates, die eine beliebige Anzahl von Elementen annehmen können
- das folgende Programm stellt zwei Variadic Templates vor
- die Templatefunktion f kann mit einer beliebigen Zahl von Argumenten umgehen
- die Templatefunktion printCommaSeparatedList, die in zwei Ausprägungen vorliegt, gibt eine beliebige Anzahl von Elementen aus
#include <iostream>
#include <string>
#include <vector>
template <typename... Args>
int f(Args... args){
return (sizeof... args);
}
template<typename T>
void printCommaSeparatedList(T value)
{
std::cout<<value<<std::endl;
}
template<typename First,typename ... Rest>
void printCommaSeparatedList(First first,Rest ... rest)
{
std::cout<<first<<",";
printCommaSeparatedList(rest...);
}
int main() {
std::cout << "\n";
auto intList= {1,2,3};
std::vector <int> intVec= {1,2,3,4,5};
std::cout << "f() has " << f() << " arguments\n";
std::cout << "f(42, 3.14) has " << f(42, 3.14) << " arguments\n";
std::cout << "f(\"one\",\"two\",\"three\",\"four\") has " << f("one","two","three","four" )
<< " arguments\n";
std::cout << "f(intVec,intList) has " << f(intVec,intList) << " arguments\n\n";
printCommaSeparatedList("Only primary template used.");
printCommaSeparatedList(42,"hello",2.3,'a');
std::cout << "\n";
}
- erzeugt die Ausgabe:
f() has 0 arguments
f(42, 3.14) has 2 arguments
f("one","two","three","four") has 4 arguments
f(intVec,intList) has 2 arguments
Only primary template used.
42,hello,2.3,a - Wie funktioniert nun das ganze?
- das entscheidende sind die drei Punkte, die entweder links
template <typename... Args>
oder rechtsint f(Args... args){
vom ParameterArgs
stehen- links von Parameter packt der Ellipsen-Operator ... das sogenannte parameter pack, rechts entpackt er es wieder
- eine Besonderheit ist der neue Operator
sizeof ...
, der direkt mit parameter packs umgehen kann - f nimmt alle Argumente an und reicht sie an den neuen Operator
sizeof ...
durch - printCommaSeparatedList, besteht besteht aus dem Template, das ein Argument verlangt und dem, das mehr als ein Argument erwartet
- beim Aufruf mit einem Argument
printCommaSeparatedList("Only primary template used.")
wird das primäre Template verwendet und der String ausgegeben - der Aufruf
printCommaSeparatedList(42,"hello",2.3,'a')
stösst das Template mit mehreren Argument an, gibt das erste Argument mit der Anweisungstd::cout<<first<<",";
aus, und ruft sich rekursiv mit der AnweisungprintCommaSeparatedList(rest...);
auf - diese Rekursion terminiert genau dann, wenn
rest
nur noch ein Element enthält, denn jetzt wird das primäre Template verwendet
- beim Aufruf mit einem Argument
- das entscheidende sind die drei Punkte, die entweder links
Ein interessanteres Beispiel für Variadic Templates ist die printfFunktion. Im Gegensatz zu ihrem C Pendant ist sie typsicher.
Konstante Ausdrücke
- Template Metaprogramming zeichnet aus, das der Code zur Compilerzeit ausgeführt wird und somit zu Laufzeit als Konstante zur Verfügung steht
- hierzu ist es notwendig, das die Ausdrücke zur Kompilezeit evaluiert werden können neues Schlüsselwort
constexpr
- durch
constexpr
können einfache Funktionen, die einen Returnwert besitzen oder auch Objekte als konstante Ausdrücke deklariert werden - Beispiel:
constexpr int square(int x) { return x * x; }
int values[square(7)];
- erst durch
constexpr
ist der Aufrufsquare(7)
möglich
Zusicherungen zur Kompilezeit
static assert
erlaubt es konstante Ausdrücke ohne Template Metaprogramming Magic zur Kompilezeit zu evaluieren- damit können Voraussetzungen an die Templateparameter geprüft werden und gegebenfalls lesbare Fehlermeldungen ausgegeben werden
static_assert(sizeof(int) == 4, "This code only works for sizeof(int) == 4")
stellt sicher, das int die Länge 4 besitzt
Weitere Features
- streng typisierte enums
- verallgemeinerte Plain Old Data (POD's) und Unions
- nullptrLiteral
- neue String Literale
R"raw string"
undU"This is a Unicode Character: \u2018."
- zwei abschliessende Klammern syntaktisch erlaubt
std::list<std::vector <int>>
: http://en.wikipedia.org/wiki/C%2B%2B0x#Angle_bracket - Integration von C99 zum Grossteil long long steht zur Verfügung
Standard Library
Thread
- C++0x besitzt eine einheitliche Threading Schnittstelle
- die Thread Schnittstelle ist eines der letzten Featues in C++0x
Memory Modell
Grundproblem
Schreibt ein Thread eine gemeinsame Variable während ein andere diese liest, ist das Verhalten nicht deterministisch.x=y=0Welchen Wert hat r1 und r2 am Ende des Programms? Können beide Werte 0 sein? sowohl Optimierung auf Hardware Ebene (Schreibepuffer) wie auch Standard Compiler Transformationen machen dies möglich (Bruch der sequentiellen Consistenz)
Thread 1 Thread 2
x=1 y=1
r1=y r2=x
Lösung:
- Locks
Thread 1 Thread 2
lock(l) lock(l)
x= 1; y=1
r1=y r2=x
unlock(l) unlock(l) - Atomics
atomic x=y=0
Thread 1 Thread 2
x=1 y=1
r1=y r2=x
- durch atomic von x und y werden die Schreibaktionen atomar und werden sofort zwischen den Threads sichtbar
- atomare Datentypen entsprechen im Standardfall von C++0x den volatilen Datentypen in Java
- die C++ Datentypen volatile haben nichts mit Threading zu tun
- das C++0x ist an das Javas Memory Modell angelehnt
- atomare Datentypen erlauben lockfreies Programmieren
- atomaren Operationen ohne sie ist Synchronisation nahezu unmöglich
- partielle Ordnung von Operationen Reihenfolge von Operationen, die der Compiler nicht verändern darf (_happens before_)
- Speichersichbarkeit Zeitpunkt, ab dem der Speicher für alle Threads den gleichen Wert besitzt
- Data Race Semantik Grundlage für Optimierungen des Compilers, sodass der Code seine Bedeutung behält
Lebenszeit
std::thread(fun)
startet einen Thread, wobei func eine Funktionspointer oder ein Funktor ist- func kann belieg viele Argumente annehmen CppNext#Variadic_Templates
class Work{
public:
void operator()(int i,std::string s,std::vector<double> v);
};
std::thread t(Work(),42,"hello",std::vector<double>(23,3.141));
join
lässt den Vaterthead auf die Beendigung des Threads warten,detach
löst die Lebenszeit des Threads vom Vater
Schutz von Daten
- es gibt verschiedene Muteces
- nicht rekursiv (
std::mutex
) - rekursiv (
std::recursive_mutex
) - nicht-rekursiv mit Zeitvorgaben (
std::timed_mutex
) - rekursiv mit Zeitvorgaben (
std::recursive_timed_mutex
)
- nicht rekursiv (
- Muteces sollten nicht direkt verwendet, sondern in
std::unique_lock<>
oderstd::lock_guard<>
gekapselt werden - Beispiel: einfacher Lock
std::mutex m;
my_class data;
void foo(){
std::lock_guard<std::mutex> lk(m);
process(data);
}
- versuche das Lock 3 Millisekunden lang zu bekommen
std::timed_mutex m;
my_class data;
void foo(){
std::unique_lock<std::timed_mutex> lk(m,std::chrono::milliseconds(3)); // wait up to 3ms
if(lk) process(data);
}
- verzöge das Locken der Ressoure und Locke alle Ressourcen gleichzeitig
struct X{
std::mutex m;
int a;
std::string b;
};
void foo(X& a,X& b){
std::unique_lock<std::mutex> lock_a(a.m,std::defer_lock);
std::unique_lock<std::mutex> lock_b(b.m,std::defer_lock);
std::lock(lock_a,lock_b);
// process a and b
}
- Vorteile
- kapseln des Locks in
std::lock_guard
bzw.std::unique_lock
Lock wird automatisch wieder freigegeben, sobald die Lock-Wächter out of scope gehen; Anwendung des RAII Idioms std::lock
verhindert Deadlocks, die durch Aufrufe der Form foo(x,y) und foo(y,x) resultieren können
beiden Muteces werden atomar gelockt
- kapseln des Locks in
Initialisierung der Daten
Das threadsicher Initialisieren von Daten ist auf mehrer Arten möglich.class MyClass{
int i;
public:
constexpr MyClass():i(0){}
MyClass(int i_):i(i_){}
};
MyClass x; // 1
void bar(){
static myClass z(42); // 2
}
MyClass* p=0;
std::once_flag p_flag; // 3
void create_instance(){
p=new MyClass(43);
}
void baz(){
std::call_once(p_flag,create_instance); // 3
}
constexpr
stellt sicher, das (1) zur Compilezeit initialisiert wird (CppNext#Konstante_Ausdrücke)- statische Variablen in einem Block Scope (2) werden threadsafe initialisiert
- durch
std::once_flag
in Kombination mitstd::call_once
wird die Funktion (3) genau nur einmal ausgeführt
Signale zwischen Threads
- durch Bedingungsvariablen (condition variables) können Thread sich über Events synchronisieren
std::mutex m;
std::condition_variable cond;
bool data_ready;
void process_data();
void foo(){
std::unique_lock<std::mutex> lk(m);
cond.wait(lk,[]{return data_ready;}); // (1)
process_data();
}
void set_data_ready(){
std::lock_guard<std::mutex> lk(m);
data_ready=true;
cond.notify_one(); // (2)
}
cond.wait(lk,[]{return data_ready;})
hebt den Lock auf, schaut nach ob data_ready gilt und fährt gegebenfalls mit dem prozessieren der Daten fortcond.notiy_one()
signalisiert, das die Daten fertig sind
Thread lokale Daten
- durch
thread_local
werden Daten definiert, die- dem Thread gehören
- ihre Lebenszeit mit dem Thread teilen
- statische Variablen in dem Sinne sind, das sie beim ersten Mal initialisiert werden
std::string foo(std::string const& s2){
std::thread_local std::string s="hello";
s+=s2;
return s;
}
- der String s beim ersten Durchlauf initialisiert und s2 erweitert
- jeder weitere Durchlauf erweitert s um den Eingabeparameter s2
Futures
Starte eine Task(Job in einem neuen Thread), warte aber nicht auf ihren Wert, sondern hole ihn später ab- Future: der Thread, der den Wert abholt
- Promise: der Thread, der den Wert liefert
std::async
template<class T, class V>
struct Accum { // simple accumulator function object
T* b;
T* e;
V val;
Accum(T* bb, T* ee, const V& v) : b{bb}, e{ee}, val{vv} {}
V operator() () { return std::accumulate(b,e,val); }
};
double compute(std::vector<double>& v){
//spawn many tasks if v is large enough
if (v.size()<10000) return std::accumulate(v.begin(),v.end(),0.0); (1)
auto f0 {std::async(Accum{&v[0],&v[v.size()/4],0.0})};
auto f1 {std::async(Accum{&v[v.size()/4],&v[v.size()/2],0.0})};
auto f2 {std::async(Accum{&v[v.size()/2],&v[v.size()*3/4],0.0})};
auto f3 {std::async(Accum{&v[v.size()*3/4],&v[v.size()],0.0})};
return f0.get()+f1.get()+f2.get()+f3.get();
}
- abhängig von der Grösse des Vektors v werden vier neue Threads gestartet und in diesen die entsprechenden Werte akkumuliert
- die ganze Berechnung wird durch
f0.get()+f1.get()+f2.get()+f3.get()
synchronisiert (geblockt), den nun werden die Ergebnisse eingesammelt - zum jetztigen Zeitpunkt ist noch nicht klar, ob
std::async
in den aktuellen C++0x Standard aufgenommen wirdstd::package_task
verpackt eine Funktion(Funktionsobjekt) auf ähnliche Weise in einer Task
std::promise
- setze den Rückgabewert der Task
set_value
oderset_exception
void asyncFun(std::promise<int> intPromise){
int result;
try {
// calculate the result
intPromise.set_value(result);
} catch (MyException e) {
intPromise.set_exception(std::copy_exception(e));
}
}
std::future
- starte die Task (2), verbinde dich mit dem Promise durch
get_future
(1) und hole den Wert mitget
(3) ab
std::promise<int> intPromise;
std::unique_future<int> intFuture = intPromise.get_future(); (1)
std::thread t(asyncFun, std::move(intPromise)); (2)
// do some other stuff
int result = intFuture.get(); // may throw MyException (3)
unique_future
besitzt im Gegensatz zushared_future
Move Semantik
Smart Pointer.
Smart Pointer sind spezielle, intelligente Zeiger, die eine gewrappte Ressource mit zusätzlicher Funktionalität ausstatten.Die drei neuen Smart Pointer
shared_ptr
, weak_ptr
und unique_ptr
lösen die bekannten auto_ptr
aus C++ ab. Sie überwachen den Lebenszyklus der Ressource nach dem RAII Idiom. Jeder dieser Smart Pointer besitzt sein spezielles Einsatzgebiet. Sowohl shared_ptr
als auch insbesondere unique_ptr
besitzen ein ähnliches Interface wie der auto_ptr
. Der auto_ptr
, der genau eine Ressource kapselt, wird als deprecated erklärt, da er zwei grosse Schwächen besitzt:
- beim Kopieren eines
auto_ptr
wird deren Inhalt mitkopiert Transfer of Ownership oder Move Semantik - er ist weder kopier- oder zuweisbar kann nicht in Standardcontainern verwendet werden
std::auto_ptr<int> auto1(new int(5));
std::auto_ptr<int> auto2(auto1);
int a= *auto1;
- auto_ptr:
- der Aufruf
int a= *auto1
ist undefiniert
Beispiel
Das Programmstd::vector< std::tr1::shared_ptr<std::string> > sharedVec;
std::tr1::shared_ptr<std::string> sharedPtr( new std::string("initial"));
sharedVec.push_back(std::tr1::shared_ptr<std::string>( sharedPtr ));
std::cout << "Values: sharedPtr sharedVec" << std::endl;
std::cout << "Initial: " << " " << *sharedPtr << " " << *sharedVec[0]
<< std::endl;
*sharedPtr="modfied";
std::cout << "Modified: " << " " << *sharedPtr << " " << *sharedVec[0]
<< std::endl;
std::cout << "use_count: " << sharedPtr.use_count() << std::endl;
{
std::tr1::shared_ptr<std::string> localSharedPtr( sharedPtr );
std::cout << "use_count: " << sharedPtr.use_count() << std::endl;
}
std::cout << "use_count: " << sharedPtr.use_count() << std::endl;
std::tr1::weak_ptr<std::string> weakPtr( sharedPtr );
std::cout << "use_count: " << sharedPtr.use_count() << std::endl;
std::unique_ptr<std::string> uniquePtrFirst( new std::string("only one") );
// std::unique_ptr<std::string> uniquePtrSecond( uniquePtrFirst); will not compile
std::unique_ptr<std::string> uniquePtrSecond( std::move(uniquePtrFirst));
std::cout << "uniquePtrFirst.get(): " << uniquePtrFirst.get() << " *uniquePtrSecond.get(): "
<< *uniquePtrSecond.get() << std::endl;
Values: sharedPtr sharedVec
Initial: initial initial
Modified: modfied modfied
use_count: 2
use_count: 3
use_count: 2
use_count: 2
uniquePtrFirst.get(): 0 *uniquePtrSecond.get(): only one
shared_ptr
- shared_ptr:
shared_ptr
können auf eine Ressource verweisen.
- Referenzzähler
- inkrementiert, falls eine neuer
shared_ptr
auf die Ressource erzeugt wird - dekrementiert, falls ein
shared_ptr
gelöscht wird (out of scope) - die Ressource wird genau dann gelöscht, wenn der Referenzähler auf 0
deterministisches Löschen der Ressource != Garbage Collection, den hier wird die Ressource nur freigegeben
- inkrementiert, falls eine neuer
- Destruktor:
- dem Konstruktor kann eine Funktion oder Funktor mitgegeben werden um die Ressource freizugeben
- für den
shared_ptr
shared_ptr(Y * p, D d);
wird im Destruktord(p)
aufgerufen
- ist kopier- und zuweisbar kann in Standard Containern verwendet werden
- Schwäche:
- zyklische Abhängigkeiten erlauben es nicht die Ressource freizugeben
weak_ptr
Bricht zyklische Refenzen auf.- bietet nur ein sehr einfaches Interface an
- modifiziert nicht den Referenzzähler
- kann selber keine Ressource besitzen
- ist nicht wirklich ein Smart Pointer
- erlaubt im Gegensatz zum
shared_ptr
undweak_ptr
kein transparenten Zugriff auf die Ressource - um auf die Ressource eines
weak_ptr
zuzugreifen, wird diese gelockt und einshared_ptr
damit initialisiert
std::weak_ptr<X> resource;
if(shared_ptr<X> px = resource.lock()){
// use *px to access the resource
}
else{
// resource has expired
}
unique_ptr
Besitz exclusive eine Ressource.- unique_ptr:
unique_ptr
ist nahezu Interfacecompatile mit demauto_ptr
Smart Pointern, unterbindet aber dessen fehlerträchtiges kopieren- entsprechend zum
auto_ptr
besitzt derunique_ptr
exclusive seine Ressource, die beim Kopieren transferiert wird Move Semantik unique_ptr
lassen sich aber nicht direkt kopieren, sondern müssen die Funktionstd::move
verwenden
auto_ptr<int> ap1(new int);
auto_ptr<int> ap2 = ap1; // OK, albeit unsafe
unique_ptr<int> up1(new int);
unique_ptr<int> up2 = up1; // compilation error: private copy constructor inaccessible
- kann in Algorithmen verwendet werden, in den nur mit Rvalue Referenzen (temporäre Objekte; Objekte ohne Namen) oder expliziten Movekonstruktoren gearbeitet wird
unique_ptr<int> up(new int)
unique_ptr<int> up1= std::move(up);
- erreicht wird dies Verhalten durch einen privaten Kopierkonstruktor und einen öffentlichen Movekonstruktor
template <class T>
class unique_ptr{
public:
unique_ptr(unique_ptr&& u); // rvalues bind here
private:
unique_ptr(const unique_ptr&); // lvalues bind here
};
Container
Tuple
Der neue Container Typstd::tuple
ist eine Verallgemeinerung des bekannten C++ Datentyps std::pair
, der Paare verschiedenes Typs binden kann. Tuple haben die folgenden Eigenschaften:
- besitzen eine feste Dimension
- kann mindestens 10 verschiedene Elemente binden
- kann in einem Standard Container verwendet werden
- Tupleinstanzen können genau dann miteinander verglichen werden, wenn sie einerseits die gleiche Länge besitzen und andererseits deren Elemente miteinander vergleichbar sind
std::tr1::tuple<std::string,int,float> tup1("first tuple",3,4.17); (1)
std::tr1::tuple<std::string,int,double> tup2= std::tr1::make_tuple("second tuple",4,1.1);(1)
std::cout << std::boolalpha;
std::cout << std::tr1::get<0>(tup1) << " and " << std::tr1::get<0>(tup2) << std::endl; (2)
std::cout << "tup1 < tup2: " << (tup1 < tup2) << std::endl; (4)
std::tr1::get<0>(tup2)= "Second Tuple"; (3)
std::cout << std::tr1::get<0>(tup1) << " and " << std::tr1::get<0>(tup2) << std::endl; (2)
std::cout << "tup1 < tup2: " << (tup1 < tup2) << std::endl; (4)
- Tuple können sowohl durch einen Kunstruktor als auch durch die Hilfsfunktion
std::tr1::make_tuple
erzeugt werden (1) - auf einzelne Elemente kann mittels
std::tr1::get<ind>(tup)
lesend (2) und gegebenfalls schreibend (3) zugegriffen werden zugegriffen - Tuple unterstützen Vergleiche (4)
first tuple and second tuple
tup1 < tup2: true
first tuple and Second Tuple
tup1 < tup2: false
Array
array
ist ein Standard Template Library (STL) konformer Container Wrapper for Arrays fester Länge. Container | statische Grösse | STL konform | kontinuierlicher Speicherbereich |
---|---|---|---|
C-Array | |||
std::vector | |||
std::array |
Das C++0x
std::array
verbindet das Laufzeitverhalten des C-Arrays mit dem Interface des C++ std:vector
. std::tr1::array <int,8> a1={1,2,3,4,5,6,7,8}; (1)
std::tr1::array <int,8> a2={1,2,3,4,5}; (1)
std::copy( a1.begin(), a1.end(), (2)
std::ostream_iterator< int >(std::cout," "));
std::cout << "\n";
std::copy( a2.begin(), a2.end(), (2)
std::ostream_iterator< int >(std::cout," "));
std::cout << "\n";
a2[7]=8; (3)
std::copy( a2.begin(), a2.end(), (2)
std::ostream_iterator< int >(std::cout," "));
std::cout << "\n";
std::cout << "std::accumulate(a2.begin(),a2.end(),0): " (2)
<< std::accumulate(a2.begin(),a2.end(),0) << "\n";
std::cout << "a2.size(): " << a2.size() << std::endl; (2)
- der Konstruktor von
std::array
verlangt ein Aggregat; fehlende Werte werden Defaultinitialisiert (1) std::array
kann in den Algorithmen der STL verwendet werden (2)- als Array erlaubt es natürlich den Indexzugriff (3)
1 2 3 4 5 6 7 8
1 2 3 4 5 0 0 0
1 2 3 4 5 0 0 8
std::accumulate(a2.begin(),a2.end(),0): 23
a2.size(): 8
Ungeordnete Assoziative Container (Hashtabelle)
Die Ungeordneten Assoziativen Container verhalten sich wie die bekannten Geordneten Assoziativen Container.Der feine Unterschied ist
- die Schlüssel der ungeordneten Datentypen sind nicht geordnet konstante Zugriffszeit ist möglich
- die geordneten Datentypen haben logarithmische Zugriffszeit
- die Elemente der Ungeordneten Assoziativen Container müssen nicht vergleichbar sein
- es gibt vier verschiedene Ungeordnete Assoziative Arrays
- unordered_set: eindeutige Schlüssel
- unordered_multiset: mehrere gleiche Schlüssel möglich
- unordered_map: nur ein (Schlüssel,Wert) Paar mit gleichem Schlüssel erlaubt
- unordered_multimap: mehrere (Schlüssel,Wert) Paare mit gleichem Schlüssel möglich
- jedem Ungeordneten Assoziativen Array steht aus C++98 ein Geordnetes Assoziatives Array gegenüber
Ungeordnete Assoziative Arrays Geordnete Assoziative Arrays std::unordered_set std::set std::unordered_multiset std::multiset std::unordered_map std::map std::unordered_multimap std::map - die Anwendung der Ungeordneten Assoziativen Arrays ist ziemlich unspektakulär, da dieser der der Geordneten Assoziativen Arrays entspricht
std::map<std::string,int> m { {"Dijkstra",1972},{"Scott",1976},{"Wilkes",1967},{"Hamming",1968} };
m["Ritchie"] = 1983;
for(auto x : m) std::cout << '{' << x.first << ',' << x.second << '}';
std::unordered_map<std::string,int> um { {"Dijkstra",1972},{"Scott",1976},{"Wilkes",1967},{"Hamming",1968} };
um["Ritchie"] = 1983;
for(auto x : um) std::cout << '{' << x.first << ',' << x.second << '}';
Reguläre Ausdrücke
Der Umgang mit regulären Ausdrücken in C++0x lässt sich in drei Schritte zerlegen.std::regex rgx(R"\d+"); (1)
std::smatch match; (2)
if (std::regex_search(std::string("123A43"), match, rgx)) (3)
std::cout << "match found after " << match.prefix() << '\n';
std::regex rgx(R"\d+")
hält den regulären AusdruckR"\d+"
bezeichnet einen Raw String Literal in C++0x- der String folgt per Default der ECMAScript Grammatik, aber auch POSIX BRE, ERE, awk, grep, egrep oder sed Syntax ist möglich
- neben
std::regex
gibts auchstd::wregex
für wide characterswchar_t
std::smatch match
erhält das Ergebnis der Suchematch[0]
: der Gesamtmatchmatch[i]
: i>0 sind die Teilmatchesposition
,suffix
,prefix
undlength
liefern weiter Informationen
std::regex_search
verarbeitet das Suchergebniss weiterstd::regex_match
: verlangt einen genauen Treffer und gibt ein Boolean zurück
std::regex_match("bd",std::regex(R"b(c*)d"))
std::regex_search
: schaut nach dem ersten geeignete Sequenzstd::regex_replace
: schaut nach einem Treffer und ersetzt ihn
Wiederholtes Suchen
Mitstd::regex_iterator
und std::regex_token_iterator
lässt sich ein Iterator über eine Eingabesequenz definieren.
std::regex_iterator
: liefert die Zeichen, die dem regulären Ausdruck entsprechenstd::regex_token_iterator
: splittet die Eingabesequenz mit Hilfe der regulären Ausdrücke Tokenizer
std::cout << std::endl;
boost::regex reg1("[^13579,]");
std::string str1="1,2,3,4,5,6,7,8,9,10,11,12";
boost::sregex_iterator it1(str1.begin(),str1.end(),reg1);
boost::sregex_iterator end1;
std::cout << "Character Stream: " << std::endl;
while (it1 != end1) std::cout << " " << *it1++;
std::cout << "\n\n\n";
boost::regex reg2(",");
std::string str2="1,2,3,4,5,6,7,8,9,10,11,12";
boost::sregex_token_iterator it2(str2.begin(),str2.end(),reg2,-1);
boost::sregex_token_iterator end2;
std::cout << "Token Stream: " << std::endl;
while (it2 != end2) std::cout << " " << *it2++;
std::cout << "\n";
std::cout << std::endl << std::endl;
it1(str1.begin(),str1.end(),reg1)
gibt einen Iterator über alle Zahlen aus"1,2,3,4,5,6,7,8,9,10,11,12"
zurück, die nicht inreg1("[^13579,]")
sindit2(str2.begin(),str2.end(),reg2,-1)
gibt einen Iterator über die kommasepariertenreg2(",")
Tokens des Strings"1,2,3,4,5,6,7,8,9,10,11,12"
zurück- Ausgabe:
Character Stream:
0 2 4 6 8 0 2
Token Stream:
1 2 3 4 5 6 7 8 9 10 11 12
Zufallszahlen
Zufallszahlen sind in vielen Bereichen notwendig:- Tests
- Spiele
- Simulationen
- Sicherheit
std::variate_generator
) besteht aus zwei Teilen. Einem Generator, der Sequenzen von Zufallszahlen erzeugt und einer Verteilung, die die Zufallszahlen in einem vorgegebenem Bereich verteilt. std::tr1::mt19937 rng; // generator
std::tr1::uniform_int<> six(1,6); // distribution
std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_int<> > dice(rng, six);
for ( int i=1; i<= 9; ++i){
std::cout << "dice["<< i << "]: " << dice() << std::endl;
}
- Ausgabe
dice[1]: 5
dice[2]: 1
dice[3]: 6
dice[4]: 6
dice[5]: 1
dice[6]: 6
dice[7]: 6
dice[8]: 2
dice[9]: 4
Type Traits
Ein Program, das ein anderes Program erzeugt, nennt sich Metaprogramm. Tut sie dies noch zur Compilezeit mit Templates, dann heißt diese Technik Static Template Metaprogramming. Dietype_traits
Bibliothek erlaubt Typabfragen, Vergleiche und Modifikationen zur Compilezeit. Damit lassen sich Algorithmen speziell auf den Datentyp zuschneiden und doch generisch aufrufen. #include <tr1/type_traits>
#include <iostream>
template <class Ty>
void multBy3Impl(Ty val, const std::tr1::true_type&){
std::cout << "Arithmetic type: ";
std::cout << 3*val << std::endl;
}
template <class Ty>
void multBy3Impl(Ty val, const std::tr1::false_type&){
std::cout << "No arithmetic type: ";
std::cout << val << val << val << std::endl;
}
template <class Ty>
void multBy3(Ty val){
multBy3Impl(val, std::tr1::is_arithmetic<Ty>());
}
int main(){
multBy3(1);
multBy3(5.5);
multBy3(std::string("Only for testing purpose. "));
}
- das Programm evaluiert zu Compilezeit, ob das Argument von
multBy3
ein arithmetischer Typstd::tr1::is_arithmetic
ist oder nicht - abhängig von dem Ergegnis wird
multBy3Impl(Ty val, const std::tr1::true_type&)
bzw.multBy3Impl(Ty val, const std::tr1::false_type&)
aufgerufen - die entsprechende Implementierung
multBy3Impl
weiß, wie sie den Datentyp zu multiplizieren hat - die Ausgabe zeigt, das der Typdispatch zur Compilezeit richtig ausgeführt wird
Arithmetic type: 3
Arithmetic type: 16.5
No arithmetic type: Only for testing purpose. Only for testing purpose.
Only for testing purpose.
Typ Abfragen
- Primary Type Categories (
is_void
,is_floating_point
,is_array
... ) - Composite Type Categories (
is_arithmetic
,is_object
,is_member_pointer
... ) - Type Properties (
is_const
,is_pod
,is_abstract
, ... )
Typ Vergleiche
- Type Relationships (
is_same
,is_convertible
,is_base_of
... )
Type Modfikationen
- Typ Transformations (
remove_const
,add_const
,remove_reference
... ) - Alignment (
alignment_of
,aligned_storage
)
Optimiertes Kopieren von Datenstrukturen
Ein generisches Kopieralgorithmus für Standardcontainer kann so aussehen:
template<typename I1, typename I2, bool b
I2 copy_imp(I1 first, I1 last, I2 out, const std::tr1::integral_constant<bool, b>&){
while(first != last){
*out = *first;
++out;
++first;
}
return out;
}
[first,last[
wird sukzessive an den Bereich [out,...[
kopiert. Dies geht mit
memcpy
deutlich schneller. template<typename T>
T* copy_imp(const T* first, const T* last, T* out, const std::tr1::true_type&){
memcpy(out, first, (last-first)*sizeof(T));
return out+(last-first);
}
memcpy
zu verwenden:
- die Iteratoren müssen Pointer sein (
const T* first, const T* last, T* out
) - die Iteratoren müssen auf die gleichen Typen verweisen (
<typename T>
enthält nur ein Typ) - die Elemente des Containers müssen einen trivialen, vom Compiler automatisch erzeugten, Zuweisungsoperator besitzen (
const std::tr1::true_type&
)
template<typename I1, typename I2>
I2 copy(I1 first, I1 last, I2 out){
typedef typename std::iterator_traits<I1>::value_type value_type;
return copy_imp(first, last, out, std::tr1::has_trivial_assign<value_type>());
}
- stösst die richtige copy_imp Implementierung an
- durch
typedef typename std::iterator_traits<I1>::value_type value_type
wird der Typ der Containerelemente bestimmt um ihn anschliessend instd::tr1::has_trivial_assign<value_type>
zu nutzen - abhängig vom Ergebniss der Evaluierung wird der optimierte oder generische Kopieralgorithmus verwendet
- das Ergebnis des Aufrufs
std::tr1::has_trivial_assign<value_type>
ist eine Klasse, so dass der Dispatch auf das vierte Argument der copy_imp Methoden vollzogen wird - der Ausdruck
typedef integral_constant<bool, true> true_type;
definiert eine Spezialisierungtrue_type
vonstd::tr1::integral_constant<bool, b>
Referenz Wrapper
Referenzen sind in C++ keine First class objects.sie können nicht kopiert werden
sie können nicht in Standardcontainern verwendet werden, da sie hierzu copy-constructible and assignable sein müssen
dieser Ausdruck lässt sich in C++ nicht kompilieren:
std::vector<int&>
Die C++0x Bibliothek
reference_wrapper
erzeugt aus Objekten vom Type T& copy-constructible and assignableObjekte. Damit lassen sich
- Objekte in Containern verwenden, die sich nicht Kopierkonstruierbar und Zuweisbar sind (Dateien, Locks oder auch Netzwerkverbindungen)
- Objekte mit Referenzsemantik in Containern verwenden
std::vector<bool> copyVec;
std::vector< std::tr1::reference_wrapper<bool> > refVec;
bool b=false;
copyVec.push_back(b);
refVec.push_back(std::tr1::ref(b));
std::cout << std::boolalpha;
std::cout << "Values: b copyVec refVec" << std::endl;
std::cout << "Initial: " << b << " " << copyVec[0] << " " << refVec[0] << std::endl;
b= true;
std::cout << "Modified: " << b << " " << copyVec[0] << " " << refVec[0] << std::endl;
- Ausgabe:
Values: b copyVec refVec
Initial: false false false
Modified: true false true
ref<T>
gibt einen T& Wrapper Objektcref<T>
gibt einen const T& Wrapper Objekt
template <typename T>
void doubleMe(T t){
++t;
};
int f=1;
std::cout << "initial value: " << f << std::endl;
doubleMe(f);
std::cout << "doubleMe(f): " << f << std::endl;
doubleMe(std::tr1::ref(f));
std::cout << "doubleMe(ref(f)) : " << f << std::endl;
- Ausgabe:
initial value: 1
doubleMe(f): 1
doubleMe(ref(f)) : 2
bind und function
Die beiden Librarysbind
und function
ergänzen sich sehr gut.
bind
- erlaubt Argumente an beliebige Positionen einer Funktion, Methode oder eines Funktionsobjektes zu binden
- das resultierende Funktionsobjekt kann direkt aufgerufen, in den Algorithmen der Standard Template Library verwendet oder in einem Funktionsobjekt
function
gespeichert werden - für Argumente ohne Wert können Platzhalter eingeführt werden
function
- ermöglicht die einfache Definition von Funktionsobjekten
- diese Funktionsobjekten sind first class functions
- erlauben die einfache Definition von Callbacks
bind
undfunction
- teilweise evaluieren jedes beliebigen Arguments mit
bind
und binden der neuen Funktion mitfunction
sehr mächtiges currying lässt sich umsetzen - Funktionsobjekte, die mit
bind
erzeugt werden, können direkt mitfunction
gebunden und später aufgerufen werden.
- teilweise evaluieren jedes beliebigen Arguments mit
double divMe(double a, double b){
return double(a/b);
}
template <typename T>
T addMe(T a, T b){
return a+b;
};
int main(){
std::cout<< "1/2.0= " << bind(divMe,1,2.0)() << std::endl; (1)
function<double(double)> myDiv= bind(divMe,_1,2.0); (2)
std::cout<< "1/2.0= " << myDiv(1) << std::endl;
function<int(int)> addInt = bind(addMe<int>, _1, 2);
std::cout << "1+2= " << addInt(1) << std::endl;
function<std::string(std::string)> addString = bind(addMe<std::string>,"first",_1); (3)
std::cout << "first + second= " << addString("second") << std::endl;
auto funcObject1= bind(addMe<std::string>, "first",_1);
std::cout << "first + second= " << funcObject1("second") << std::endl;
auto funcObject2= bind(addMe<std::string>, _1, _2);
std::cout << "first + second= " << funcObject2("first","second") << std::endl;
std::vector<int> myVec{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
std::copy_if( myVec.begin(), myVec.end(), (4)
std::ostream_iterator<int>( std::cout, ", " ),
bind( std::logical_and<bool>(),
bind( std::greater <int>(),_1,9 ),
bind( std::less <int>(),_1,16 )));
std::cout<< std::endl;
/* compiles only with gcc 4.5 (5)
std::copy_if( myVec.begin(), myVec.end(),
std::ostream_iterator<int>( std::cout, ", " ),
[](int a){ return (a>9)&&(a<16);});
std::cout << std::endl;
*/
}
bind(divMe,1,2.0)()
definiert einen Funktionskörper, bindet die Argumente 1 und 2.0 und ruft ihn aufbind(divMe,_1,2.0)
bindet das zweite Argument mit 2.0, führt eine Platzhalter _1 für das erste Argument ein, bindet das Funktionsobjekt anfunction<double(double)>
und führt es anschliessend mitmyDiv(1)
ausauto funcObject1
erleichert die Definition von Funktionsobjekten- zuviele geschachtelte bind Ausdrücke (ich habe die ganzen Namespaces der Lesbarkeit wegen bekannt gemacht), können verwirrend wirken
- diese Funktion bietet die gleiche Funktionalität wie (4) an
1/2.0= 0.5
1/2.0= 0.5
1+2= 3
first + second= firstsecond
first + second= firstsecond
first + second= firstsecond
10, 11, 12, 13, 14, 15,
Weitere Bibliotheken
- time utilities
- return Wert Evaluierung von Funktionsobjekten mit return_of; die Funktonalität wird schon durch die Alternative Funktionssyntax mit decltype angeboten
- einfache Aufrufwrapper mit
mem_fn
; die Funktionalität wird schon durch std::bind angeboten
Compilerunterstützung
- die bekannten Compiler: http://wiki.apache.org/stdcxx/C++0xCompilerSupport
- gcc im Besonderen: http://gcc.gnu.org/projects/cxx0x.html
- die tr1 Erweiterungen stehen mit dem aktuellen gcc fast vollständig zur Verfügung
- lediglich die reguläre Expression und threading Bibliotheken stehen noch nicht zur Verfügung verwendete boost Bibliotheken, die als Grundlage für die tr1 Bibliotheken dienen
Weitere Informationen
- Wikipedia: http://en.wikipedia.org/wiki/C%2B%2B0x
- Bjarne Stroustrup, C++0x FAQ : http://www2.research.att.com/~bs/C++0xFAQ.html
- Danny Kalev,"C++0X: The New Face of Standard C++": http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=216
- Aktueller C++0x Standard Entwurf: http://www2.research.att.com/~bs/SC22-N-4411.pdf
Verarbschiedung des Standards
Mail am 15.03.2010 an comp.lang.c++.moderated von Herb Sutter.- March 2010 ISO C++ standards meeting
...
1. Approved Final Committee Draft (FCD) for C++0x
The biggest news is that this afternoon we voted in the final
remaining feature changes to C++0x, and to much applause then
unanimously approved the text for international ballot as a Final
Committee Draft (FCD). FCD means that, assuming no surprises, we
intend to do only bug fixes and editorial corrections for the next
year or so, and then ballot a final standard. If we can do that,
assuming all goes well, C++0x could officially be published as soon as
next year as ISO C++ 2011, and we can stop with the "x-is-hex" jokes
and just start calling it C++11.
This is a big milestone, and it was achieved thanks to removing a
couple of controversial features last summer and a whole lot of work
by the ISO C++ committee members over the past six months in
particular. That work includes countless hours spent between our full
face-to-face meetings at face-to-face ad-hoc meetings to swat library
bugs, teleconferences on resolving core language questions, and
triple-
digit person-hours invested in four teleconferences during December-
February purely about C and C++ compatibility that have greatly helped
to identify and attempt to resolve minor areas of divergence between
the C++0x draft standard and the C1x draft standard (as both are now
in progress; C1x is targeting completion and publication in 2012).
All in all, your committee members have put in an enormous amount of
effort to bring this in, and the draft is in far better shape for this
meeting than anyone could have expected last summer. For comparison,
in my and several others'; opinions, it's in better shape than the FCD
of the C++98 standard.
...
Weiterlesen...