C++0x

Surprisingly, C++0x feels like a new language: The pieces just fit together better 
than they used to and I find a higher-level style of programming more natural than before
and as efficient as ever. (Bjarne Stroustrup)
right C++0x bricht nicht mit C++ Code.

Historie

  • Zeitachse C++:
    C__Timeline.gif
  • 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

Den

Prinzipien 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.
bleibt C++0x treu und legt insbesondere Wert auf

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 right 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 auch void 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&&); und MyVector& 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:
      MoveSemantic.gif
    • Copy Semantik:
      CopySemantic.gif

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 loop for (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}
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 1
  • std::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 right 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 right 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 rechts int f(Args... args){ vom Parameter Argsstehen
      • 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 Anweisung std::cout<<first<<","; aus, und ruft sich rekursiv mit der Anweisung printCommaSeparatedList(rest...); auf
      • diese Rekursion terminiert genau dann, wenn rest nur noch ein Element enthält, denn jetzt wird das primäre Template verwendet
Pattern Matching und rekursiver Aufrufe sind elementare Bausteine der funktionalen Programmierung, die hier zur Anwendung kommen.

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 right 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 Aufruf square(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

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=0

Thread 1 Thread 2
x=1 y=1
r1=y r2=x

Welchen Wert hat r1 und r2 am Ende des Programms? Können beide Werte 0 sein? right sowohl Optimierung auf Hardware Ebene (Schreibepuffer) wie auch Standard Compiler Transformationen machen dies möglich (Bruch der sequentiellen Consistenz)
Lösung:
  1. Locks
    Thread 1                 Thread 2
    lock(l) lock(l)
    x= 1; y=1
    r1=y r2=x
    unlock(l) unlock(l)
  2. 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
Ein Memory Modell besteht aus:
  1. atomaren Operationen right ohne sie ist Synchronisation nahezu unmöglich
  2. partielle Ordnung von Operationen right Reihenfolge von Operationen, die der Compiler nicht verändern darf (_happens before_)
  3. Speichersichbarkeit right Zeitpunkt, ab dem der Speicher für alle Threads den gleichen Wert besitzt
  4. Data Race Semantik right Grundlage für Optimierungen des Compilers, sodass der Code seine Bedeutung behält
persons Das Double Checked Locking Pattern ist eine optimierte Spezialform des SingletonPattern in Multithreaded Umgebungen. Es ist aber ohne eine Memory Modell nicht Threadsicher. right http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

Lebenszeit

  • std::thread(fun) startet einen Thread, wobei func eine Funktionspointer oder ein Funktor ist
  • func kann belieg viele Argumente annehmen right 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
    1. nicht rekursiv (std::mutex)
    2. rekursiv (std::recursive_mutex)
    3. nicht-rekursiv mit Zeitvorgaben (std::timed_mutex)
    4. rekursiv mit Zeitvorgaben (std::recursive_timed_mutex)
  • Muteces sollten nicht direkt verwendet, sondern in std::unique_lock<> oder std::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
      right 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
      right beiden Muteces werden atomar gelockt

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 mit std::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 fort
  • cond.notiy_one() signalisiert, das die Daten fertig sind

Thread lokale Daten

  • durch thread_localwerden 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::asyncin den aktuellen C++0x Standard aufgenommen wird
    • std::package_task verpackt eine Funktion(Funktionsobjekt) auf ähnliche Weise in einer Task
std::promise
  • setze den Rückgabewert der Task set_value oder set_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 mit get (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 zu shared_future Move Semantik
Unter http://www.stdthread.co.uk/doc/headers.htmlist die Threading API schön beschrieben.

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:
  1. beim Kopieren eines auto_ptr wird deren Inhalt mitkopiert right Transfer of Ownership oder Move Semantik
  2. er ist weder kopier- oder zuweisbar right 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:
    auto_ptr.gif
  • der Aufruf int a= *auto1 ist undefiniert

Beispiel

Das Programm
std::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;
liefert die folgende Ausgabe:
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.gif
Mehrere 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
  • 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 Destruktor
      d(p)
      aufgerufen
  • ist kopier- und zuweisbar right 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 und weak_ptr kein transparenten Zugriff auf die Ressource
  • um auf die Ressource eines weak_ptr zuzugreifen, wird diese gelockt und ein shared_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.gif
  • unique_ptr ist nahezu Interfacecompatile mit dem auto_ptr Smart Pointern, unterbindet aber dessen fehlerträchtiges kopieren
  • entsprechend zum auto_ptr besitzt der unique_ptr exclusive seine Ressource, die beim Kopieren transferiert wird right Move Semantik
  • unique_ptr lassen sich aber nicht direkt kopieren, sondern müssen die Funktion std::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 Typ std::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)
Die Ausgabe:
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.

Containerstatische GrösseSTL konformkontinuierlicher Speicherbereich
C-Array DONE   DONE
std::vector   DONE DONE
std::array DONE DONE DONE


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)
Die Ausgabe:
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
  1. die Schlüssel der ungeordneten Datentypen sind nicht geordnet right konstante Zugriffszeit ist möglich
  2. die geordneten Datentypen haben logarithmische Zugriffszeit
  3. die Elemente der Ungeordneten Assoziativen Container müssen nicht vergleichbar sein
Die Hashtabelle schafften es nicht mehr in in C++98 Standard right viele Compilerbauer boten ihre eigene Erweiterungen an rightNamen wie hash_map waren schon vergeben
  • 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 ArraysGeordnete 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';
  1. std::regex rgx(R"\d+")hält den regulären Ausdruck
    • R"\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 auch std::wregex für wide characters wchar_t
  2. std::smatch matcherhält das Ergebnis der Suche
    • match[0]: der Gesamtmatch
    • match[i]: i>0 sind die Teilmatches
    • position , suffix , prefix und length liefern weiter Informationen
  3. std::regex_searchverarbeitet das Suchergebniss weiter
    • std::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 Sequenz
    • std::regex_replace: schaut nach einem Treffer und ersetzt ihn

Wiederholtes Suchen

Mit std::regex_iterator und std::regex_token_iteratorlässt sich ein Iterator über eine Eingabesequenz definieren.
  • std::regex_iterator: liefert die Zeichen, die dem regulären Ausdruck entsprechen
  • std::regex_token_iterator: splittet die Eingabesequenz mit Hilfe der regulären Ausdrücke right 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 in reg1("[^13579,]") sind
  • it2(str2.begin(),str2.end(),reg2,-1) gibt einen Iterator über die kommaseparierten reg2(",") 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
Der Zufallszahlengenerator (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
Ein Überblick über verschiedene Generatoren und Verteilungen ist unter http://www.boost.org/doc/libs/1_41_0/libs/random/index.html. Dies entspricht annähernd der Funktionalität von C++0x.

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. Die type_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 Typ std::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 )
Der ganze Rest an Funktionen kann unter http://msdn.microsoft.com/en-us/library/bb982077.aspxnachgelesen werden.
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;

}
Jedes Element des Bereiches [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);
}
Die Datenstrukturen müssen aber hinreichend einfach sein um memcpyzu 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& )
Ein Aufruf der Form
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 in std::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 Spezialisierung true_type von std::tr1::integral_constant<bool, b>

 

Referenz Wrapper

Referenzen sind in C++ keine First class objects.
    right sie können nicht kopiert werden
    right sie können nicht in Standardcontainern verwendet werden, da sie hierzu copy-constructible and assignable sein müssen
    right 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
reference_wrapper ist ein copy-constructible and assignable Wrapper um das Objekt vom Typ T&.

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;
right refVec[0] ist eine Referenz auf b und wird durch b= true modifiziert
  • Ausgabe:
    Values:      b       copyVec    refVec
    Initial: false false false
    Modified: true false true
Die Hilfsfunktion
  • ref<T> gibt einen T& Wrapper Objekt
  • cref<T> gibt einen const T& Wrapper Objekt
zurück.
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 Librarys bind und functionergä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 und function
    • teilweise evaluieren jedes beliebigen Arguments mit bind und binden der neuen Funktion mit function right sehr mächtiges currying lässt sich umsetzen
    • Funktionsobjekte, die mit bind erzeugt werden, können direkt mit function gebunden und später aufgerufen werden.
Die Funktionalität von bind kann oft durch lambda Funktionen ausgedrückt, die von function wird durch auto angeboten.

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;
*/

}
  1. bind(divMe,1,2.0)() definiert einen Funktionskörper, bindet die Argumente 1 und 2.0 und ruft ihn auf
  2. bind(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 an function<double(double)> und führt es anschliessend mit myDiv(1) aus
  3. auto funcObject1 erleichert die Definition von Funktionsobjekten
  4. zuviele geschachtelte bind Ausdrücke (ich habe die ganzen Namespaces der Lesbarkeit wegen bekannt gemacht), können verwirrend wirken
  5. diese Funktion bietet die gleiche Funktionalität wie (4) an
Ausgabe:
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

Weitere Informationen

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.
    ...

 

Mentoring

Stay Informed about my Mentoring

 

Rezensionen

Tutorial

Besucher

Heute 616

Gestern 3357

Woche 12179

Monat 39496

Insgesamt 3892210

Aktuell sind 29 Gäste und keine Mitglieder online

Kubik-Rubik Joomla! Extensions

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare