Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++11 #pip

C++11 #pip

Avatar for Enrico Pilotto

Enrico Pilotto

April 26, 2013
Tweet

More Decks by Enrico Pilotto

Other Decks in Programming

Transcript

  1. Risorse su C++11 • Un documento molto vicino allo standard

    • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf • Documenti ufficiali della commissione • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/ • FAQ sul C++11 di Bjarne Stroustrup • http://www.stroustrup.com/C++11FAQ.html 2
  2. Un po’ di storia • 1979: Bjarne Stroustrup inventa il

    C++ ("C with classes") • 1998: Primo standard ISO (C++98) • 2003: Revisione dello standard (C++03) • 2011: Secondo standard ISO (C++11 aka C++0x) 3
  3. #include <iostream> int main () { std::cout << "Hello world!"

    << std::endl; } C++11 con gcc • Il mio primo programma in C++11 !! • Compilare con g++ --std=c++11 hello.cpp 4
  4. #include <vector> #include <complex> std::vector<std::complex<double>> v; // error in C++98,

    ok in C++11 std::vector<std::complex<double> > w; // ok in both C++98 and C++11 >> Notare lo spazio 5
  5. #include <vector> std::vector<int> const v = {1, 2, 3, 4,

    5}; #include <map> std::map<int, std::string> const ids = { {23, "Andrea"}, {49, "Camilla"}, {96, "Ugo"}, {72, "Elsa"} }; #include <vector> std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); #include <map> std::map<int, std::string> ids; ids.insert(std::make_pair(23, "Andrea")); ids.insert(std::make_pair(49, "Camilla")); ids.insert(std::make_pair(96, "Ugo")); ids.insert(std::make_pair(72, "Elsa")); Nel C++98 inizializzare strutture dati spesso è una rottura In C++11 questa operazione è molto più semplice Initializer lists 6 const-correctness
  6. void f (std::initializer_list<int> l) { for (std::initializer_list<int>::const_iterator it = l.begin();

    it != l.end(); ++it) std::cout << *it << std::endl; } template<typename T> class vector { vector(std::initializer_list<T> l) { ... }; }; Initializer lists 7 • {1,2,3,4,5} ha tipo std::initializer_list<T> • Da considerare come un contenitore read-only • ha i metodi size(), begin(), end(), ecc... • Elementi devono essere dello stesso tipo
  7. vector<int> v {1, 2, 3}; // calls vector<int>::vector(initializer_list<int>) vector<int> v

    = {1, 2, 3}; // idem complex<double> c(1.0, 2.0); // calls complex<double>::complex(double, double); complex<double> c {1.0, 2.0}; // idem complex<double> c = {1.0, 2.0}; // idem struct X { int i, j; }; X x {1, 2}; // x.i == 1 and x.j == 2, only C++11 X x = {1, 2}; // idem, C++98 and C++11 Uniform initialization syntax 8 • Usare le grafe {} per ogni tipo di inizializzazione • Liste di inizializzazione • Costruttori • Aggregati (si può anche in C++98)
  8. int c(1.5); // ok, but truncation, c == 1 int

    c = 1.5; // idem int c{1.5}; // error int c{1.}; // error, double to integer is always considered narrowing char c(7); // ok, c == 7 char c(256); // ok, but c == 0 (assuming a char has 8 bits) char c{256}; // error, the bit representation of 256 doesn't fit in a char Uniform initialization syntax 9 • Ulteriore vantaggio: no narrowing • Attenzione: i costruttori con liste di inizializzazione possono essere diversi da altri costruttori vector<int> v{2}; // calls vector<int>::vector(initializer_list<int>) // v.size() == 1, v[0] == 2 vector<int> v(2); // calls vector<int>::vector(size_t) // v.size() == 2, v[0] == 0, v[1] == 0
  9. std::map<std::string, int> m; std::map<std::string, int>::const_iterator iter = m.begin(); Auto 10

    Perchè devo dire al compilatore di che tipo è iter? Lui già lo sa! std::map<std::string, int> m; auto iter = m.cbegin(); Il tipo auto significa che il tipo di quella variabile viene dedotto dal compilatore
  10. auto a; // error, no initializer auto i = 0;

    // i has type int auto d = 0.; // d has type double const auto e = 0.; // e has type const double const auto& g = 0.; // g has type const double& auto f = 0.f; // f has type float auto c = "ciao"; // c has type const char* auto p = new auto(1); // p has type int* auto int i; // OK in C++98, error in C++11 Auto 11 template<typename T, typename U> void f (T t, U u) { ... ??? s = t * u; // what's the type of s? ... } template<typename T, typename U> void f (T t, U u) { ... auto s = t * u; ... }
  11. std::vector<Account> v; for (std::vector<Account>::const_iterator it = v.begin(); it != v.end();

    ++it) { print(*it); } for (std::vector<Account>::iterator it = v.begin(); it != v.end(); ++it) { update(*it); } Range for 12 C++11 semplifica la sintassi per iterare su una sequenza: std::vector<Account> v; for (Account a: v) { // or, rather, Account const& print(a); } for (auto a: v) { // or, rather, auto const& print(a); } for (auto& a: v) { update(a); } • Un range può essere: • un array (int a[10]) • una classe C che implementa i metodi C::begin() e C::end() • una classe C in cui è possibile fare begin(C) ed end(C)
  12. f(int); f(int*); f(0); // calls f(int) f(nullptr); // calls f(int*)

    int* p = nullptr; int* q = 0; // still valid and p == q nullptr 13 • nullptr sta per il puntatore nullo • nullptr non è un intero (std::nullptr_t) int i = nullptr; // error • NULL non era una soluzione // stddef.h #ifdef (__cplusplus) #define NULL 0
  13. struct C { int i; C(): i(5) {} C(int ii):

    i(ii) {} }; C c1; // c1.i == 5 C c2(3); // c2.i == 3 In-class member initializers 14 Permettono di specificare un'espressione di inizializzazione per i campi dati non statici di una classe. struct C { int i = 5; C() {} C(int ii): i(ii) {} }; C c1; // c1.i == 5 C c2(3); // c2.i == 3 L'inizializzazione nei costruttori ha la precedenza
  14. double global_d; std::string get_string() { return "hello, world!"; } double

    get_double() { global_d = 3.; return 1.;} class A { int i = 5; std::string s = get_string(); double d = get_double(); public: A(): d(2.) {} }; A a; // a.i == 5, a.s == "hello, world!", a.d == 2., global_d == 0 In-class member initializers 15 Attenzione side-effect
  15. class C { std::string k; int v; void init(std::string const&

    k, int v) { this.k = k; this.v = v; // validate k and v } public: C(std::string const& k, int v) { init(k, v); } C(int v) { init("default", v); } }; Delegating constructors 16 Un costruttore può delegare ad un altro costruttore class C { std::string k; int v; public: C(std::string const& k, int v) : k(k), v(v) { // validate k and v } C(int v) : C("default", v) {} }; • Non c'è bisogno di funzioni ausiliarie
  16. // a non-copyable class in C++98 struct C { C()

    {} private: C(C const&); C& operator=(C const&); }; C c; C c1(c); // error (at compile or link time) C c2; c2 = c; // error (at compile or link time) Deleted functions 17 Dire al compilatore di non generare funzioni non esplicitamente dichiarate // a non-copyable class in C++11 struct C { C() {} C(C const&) = delete; C& operator=(C const&) = delete; }; C c; C c1(c); // error (at compile time) C c2; c2 = c; // error (at compile time) • = delete deve essere usato alla prima dichiarazione della funzione
  17. void f(double); f(1.); // ok f(1); // ok, calls f

    passing double(1) void f(int) = delete; f(1); // error Deleted functions 18 Il meccanismo è generico: ogni funzione può essere cancellata
  18. Defaulted functions 19 struct C { C() = default; C(C

    const&) = default; C& operator=(C const&) = default; }; C c; C c1(c); c2 = c; • Dire esplicitamente al compilatore che le funzioni di default da generare sono ok • per forzare la generazione di una funzione • per documentazione Si ricorda che il costruttore di default viene generato solo se non vengono dichiarati altri costruttori
  19. Raw string literals 20 cout << "\\\\\""; // prints \\"

    (3 characters) cout << R"(\\")"; // idem, but easier cout << "\n"; // prints a newline cout << R"(\n)"; // prints \n (2 characters) // how to print )" (2 characters) ? cout << R"()")"; // error cout << R"*+()")*+"; // ok • Un literale stringa che non riconosce le sequenze di escape ('\n', '\\', '\"', ...) • Migliore agilità su stringhe di regexp, xml ecc... sequenza quasi arbitraria di caratteri
  20. 123 // int 123U // unsigned int 123L // long

    int 12.3 // double 12.3F // float 12.3L // long double 'h' // char "hello" // char[6] User-defined literals 21 C++98 sono ammessi literali solo per i tipo built-in C++11 supporta literali per i tipo definiti dall'utente // for example 123s // seconds 12us // microseconds 42.1km // kilometers 23.3m2 // squared meters "hi"s // as an std::string .5i // imaginary 10101000b // binary Speed v1 = 9.8m / 1s; // ok Speed v2 = 100m / 1s2; // error Speed v3 = 100 / 1s; // error Acceleration a = v1 / 1s; // ok migliora type safety
  21. std::complex<double> operator "" i(long double d) { return {0, d};

    } std::string operator "" s(char const *s, size_t size) { return std::string(s, size); } User-defined literals 22 Usare il literal operator • I suffissi che non iniziano per '_' sono riservati per futura standardizzazione • I literal operator devono essere non-member functions
  22. const char* unsigned long long int long double char wchar_t

    char16_t char32_t const char*, std::size_t const wchar_t*, std::size_t const char16_t*, std::size_t const char32_t*, std::size_t User-defined literals 23 §13.5.8/3 says: "The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:"
  23. enum E1 { red }; // ok emum E2 {

    red }; // error enum class 24 • enum in C++98 ha diversi problemi • assenza di strong scoping • conversioni implicita a int • impossibilità di specificare il tipo enum Color { red, green, blue }; int c = red; // ok, c == 0 void f(int); f(green); // ok sizeof(Color); // 4 (!) bytes on my machine
  24. enum class E1 { red }; emum class E2 {

    red }; // ok enum class 25 • enum class in C++11 risolve i problemi: • strong scoping • no conversione implicita a int • possibilità di specificare il tipo enum class Color { red, green, blue }; int c = red; // error void f(int); f(green); // error f(Color::green); // error void g(Color); g(green); // error g(Color::green); // ok enum class Color: char { red, green, blue }; sizeof(Color); // 1 byte on any machine!
  25. static_assert(sizeof(long) >= 8, "64-bit code generation required for this library.");

    Static assertions 26 Controlla che espressioni booleane costanti siano soddisfatte a compile time. • Si può inserire ovunque nel codice • Non ci sono overhead a runtime
  26. constexpr 27 • Un modo per permettere che alcune espressioni

    costanti siano calcolate a compile-time invece che a run-time • benefici in termini di performance constexpr int multiply (int x, int y) { return x * y; } // the compiler may evaluate this at compile time constexpr int val = multiply (10, 10); constexpr int get_array_size (int multiplier) { return 10 * multiplier; } // no macro int my_array[get_array_size (3)]; constexpr unsigned long long int fib (unsigned long long int n) { return n > 1 ? fib (n - 1) + fib (n - 2) : n; } constexpr unsigned long long int x = fib (100); // 0s unsigned long long int y = fib (100); // a lot of time!! funzioni constexpr possono essere chiamata anche a run- time
  27. constexpr 28 • Una funzione constexpr deve seguire rigide regole:

    • consiste in un singolo return statement (a meno di typedef, using, static_assert) • può chiamare solo altre funzioni constexpr • può far riferimento solamente a variabili globali constexp class Circle { int x, y, r; public: // sorry, unimplemented: use of the value of the object being // constructed in a constant expression (gcc 4.7) constexpr Circle (int x, int y, int radius) : x (x), y (y), r (r) {} constexpr double get_area () { return r * r * 3.1415926; } }; // You wanted to construct a circle at compile time and get its area? constexpr Circle c (0, 0, 10); constexpr double area = c.get_area (); se dichiaro una variabile constexpr allora sarà anche costante
  28. Lambda expressions 29 • Un modo semplice e veloce per

    creare funzioni anonime • Molto utile da passare come parametro a funzioni (algoritmi, thread...) vector<int> v = {-2, -3, 4, 1}; sort(v.begin(), v.end()); // default sort, v == {-3, -2, 1, 4} bool abs_compare_f(int l, int r) const { return abs(l) < abs(r); } struct abs_compare { bool operator()(int l, int r) const { return abs(l) < abs(r); } }; abs_compare functor; sort(v.begin(), v.end(), abs_compare_f); // C++98 with function, v == {1, -2, -3, 4} sort(v.begin(), v.end(), abs_compare()); // C++98 sort(v.begin(), v.end(), functor); // C++98 with functor (function object), v == {1, -2, -3, 4} sort(v.begin(), v.end(), [](int l, int r) { return abs(l) < abs(r); }); // C++11, v == {1, -2, -3, 4}
  29. Lambda expressions 30 • In una funzione anonima è possibile

    importare l'ambiente • le variabili che vengono usate nel corpo della funzione devono essere "catturate" ed importate [] // capture nothing [&] // capture all by reference [=] // capture all by value [=, &i] // capture all by value, but i by reference [&, =i] // caputre all by reference, but i by value Viene importato SOLO quello che viene usato. double min_salary = 1000.0; find_if(employees.begin(), employees.end(), [=](Employee const& e) { return e.salary() < min_salary; });
  30. Copy vs Move 31 • In (1) la copia è

    necessaria (costruttore di copia) • In (2) la copia è una perdita di tempo: viene creato un oggetto temporaneo, poi viene copiato su s3 ed infine l'oggetto temporaneo viene distrutto. • Gli oggetti sono chiamati lvalues (quelli di cui puoi ottenere l'indirizzo di memoria. • Gli oggetti temporanei sono chiamati rvalues (non puoi ottenere il loro indirizzo di memoria) string s1(...); string s2(s1); // (1) string s3(s1 + s2); // (2)
  31. Copy vs Move 32 class string { char* s; public:

    // copy string(string const& other) { size_t size = strlen(other.s) + 1; s = new char(size); memcpy(s, other.s, size); } ~string() { delete [] s; } // move string(??? other): s(other.s) { other.s = nullptr; } string operator+(string const& ls, string const& rs); };
  32. rvalue references 33 • In C++98 non c'è modo di

    fare l'overload di funzioni che hanno come argomenti rvalue • C++11 introduce l'rvalue reference (T&&) class string { // move constructor string(string&& other) : s(other.s) { s = nullptr; } }; lvalues possono essere trasformati esplicitamente in rvalues: std::string s2(std::move(s1)); // I don't care about s1 anymore s1.size(); // undefined string s1(...); string s2(s1); // calls std::string(std::string const&) string s3(s1 + s2); // calls std::string(std::string&&)
  33. Special functions 34 • Una classe in C++11 ha quindi

    5 funzioni speciali: class C { T(T const&); // copy constructor T& operator=(T const&); // copy assignment T(T&&); // move constructor, new in C++11 T& operator=(T&&); // move assignment, new in C++11 ~T(); // destructor }; • tip: se hai bisogno di ridefinire anche una sola di queste funzioni allora molto probabilmente avrai bisogno di ridefinirle tutte. • Considerare l'utilizzo di =delete e =default. // non-copyable, movable class C { T(T const&) = delete; T& operator=(T const&) = delete; T(T&&) = default; T& operator=(T&&) = default; ~T() = default; };
  34. Variadic template 35 • Permette di creare template con un

    numero qualsiasi di argomenti • Come definire una tupla? • Una tupla è un'eterogenea collezione di dimensione fissa di valori std::tuple<int, Employee, std::string> r = { 12345, Employee("Enrico"), string("Pilotto") }; // 3 elements std::tuple<Point, Length, Color, Thickness> l = { Point(1.,2.), 12mm, Red, .1in }; // 4 elements
  35. Variadic template 36 • Usare la ricorsione: caso generale +

    uno o più casi base // basic case, must be decleared before general case. template<typename T> void print(T t) { cout << t; } // general case template<typename T, typename... R> // template type parameter pack void print(T value, R... rest) // function with paramenter pack { cout << value << ' '; print(rest...); // recursive call, parameter expansion } print(1, 2.9, "hello", '\n'); // T = int, R = {double, char const*, char} print(2.9, "hello", '\n'); // T = double, R = {char const*, char} print('\n'); // basic case, T = char
  36. Memory model 37 • Nel C++11 viene introdotto un modello

    di gestione della memoria che contempla l'esecuzione di un programma multi-thread • Un thread è un singolo flusso di controllo del programma • Ogni thread può potenzialmente accedere a qualsiasi oggetto e funzione del programma • L'interliving tra i vari thread è sconosciuto (scheduler)
  37. Memory model 38 • C++11 garantisce che due thread possono

    aggiornare ed accedere a zone di memoria separate senza interferire l'uno con l'altro • Per gli altri casi bisogna sincronizzare i thread • In caso contrario l'andamento del programma è imprevedibile • Situazioni di race condition si possono ottenere anche per trasformazioni del codice fatte dal compilatore o dal processore per questioni di performance
  38. Riepilogo • >> • Initializer lists • auto • range

    for • nullptr • in-class member initializers • Delegating constructors • Deleted functions • Defaulted functions • long long • Raw string literals • enum class • Static assertions • constexpr • Lambda expressions • rvalue references • Move constructors • decltype • Trailing return type • Type aliases (using) • Variadic template 39
  39. Next talk • parte II: The Standard Library • Nuove

    strutture dati • Nuovi algoritmi • Espressioni regolari • Generatori di numeri casuali • Bind • Smart pointer • Thread • Chrono 40
  40. ?