C++11 Nowy standard C++

Download Report

Transcript C++11 Nowy standard C++

C++11 / C++14 - wybrane elemnty nowego standardu

Roman Starosolski Przedmiot monograficzny: Metody i Narzędzia Wytwarzania Oprogramowania

Standardy

1983: C with classes -> C++

z virtual, bez klas abstrakcyjnych, dziedziczenia wielobazowego, szablonów, wyjątków, STL …

C++98/C++03

Pierwszy formalny standard i jego korekta

C++TR1 (2007)

Rozszerzona biblioteka standardowa, nieobligatoryjny

C++0x/C++11

Temat dzisiejszego wykładu

C++14 (minor release)

Temat dzisiejszego wykładu

Ciekawe linki zamiast literatury

[1] C++11 FAQ by Bjarne Stroustrup http://www2.research.att.com/~bs/C++0xFAQ.html

[2] C++11 wiki page (jest też wersja PL) http://en.wikipedia.org/wiki/C%2B%2B11 [3] C++14 wiki page http://en.wikipedia.org/wiki/C%2B%2B14 [4] Zaskakująco krytyczny wobec C++11 wywiad z Nicolai Josuttisem http://www.informit.com/articles/article.aspx?p=1846582 [5] An Introduction to Variadic Templates in C++0x by Anthony Williams http://www.devx.com/cplus/Article/41533/0/page/1

Cele opracowania nowej wersji języka

Za: C++11 FAQ by Bjarne Stroustrup [1] – Make C++ a better language for systems programming and library building -- that is, to build directly on C++'s contributions to programming, rather than providing specialized facilities for a particular sub-community (e.g. numeric computation or Windows-style application development).

– Make C++ easier to teach and learn -- through increased uniformity, stronger guarantees, and facilities supportive of novices (there will always be more novices than experts).

Przeznaczenie języka

Better C Data abstraction tool Object-oriented programming tool Generic programming tool

NEW !

auto

Stare znaczenie (zm. automatyczna) nie obowiązuje!

Nowe znaczenie – typ ma zostać określony przez kompilator na podstawie wyrażenia inicjalizującego

auto

list < int > l; for( int i=0; i<10; i++) l.push_back(i); int max = *l.begin(); for ( list::iterator if (max<*p) max=*p; p=++l.begin(); p!=l.end(); ++p) cout<<"max: "<

auto

list < int > l; for( auto i=0; i<10; i++) l.push_back(i); auto max = *l.begin(); for ( auto p = ++l.begin(); p!=l.end(); ++p) if (max<*p) max=*p; cout<<"max: "<

auto

Przykład za [1] template void multiply(const vector& vt, const vector& vu) { // ...

auto tmp = vt[i]*vu[i]; // ...

}

auto (C++14)

Dozwolony typ auto jako typ zwracany przez funkcję pod warunkiem że typ wywnioskowany z wszystkich return jest taki sam i że da się wyznaczyć - przykład za[3] auto Correct(int i) { if (i == 1) return i; // return type deduced as int else return Correct(i-1)+i; // ok to call it now } auto Wrong(int i) { if (i != 1) return Wrong(i-1)+i; // Too soon to call this. // No prior return statement.

else return i; // return type deduced as int }

decltype

Pobierz typ wyrażenia, wyrażenie nie jest wartościowane (niektóre kompilatory udostępniały wcześniej op. typeof) Przykład za [1] void f(const vector& a, vector& b) { typedef decltype(a[0]*b[0]) Tmp; for (int i=0; i

} // ...

}

Alternatywna składnia deklaracji funkcji

auto jako deklarowany typ zwracany do wyznaczenia na podstawie typu argumentów – Ze względu na zakres (scope) samo „auto” nie wystarczy Przykład za [1] template auto mul(T x, U y) -> decltype(x*y) { return x*y; } – „auto” obowiązkowe, zamiast „decltype(…)” można jawnie typ

Funkcje lambda (a.k.a

. wyrażenia lambda, „lambdy”)

Lokalna, nienazwana funkcja, do użycia jako argument szablonu (np. Algorytmów STL) zamiast zwykłej funkcji lub obiektu funkcyjnego [&](int a, int b) { return v[a].name

Funkcje lambda (a.k.a

. wyrażenia lambda, lambdy)

Przykład za [1] (zmodyfikowany) void f(vector& v) { vector indices(v.size()); int count = 0; generate(indices.begin(),indices.end(), [&count](){ return count++; } ); // sort indices in the order determined by the name field of the records: std::sort(indices.begin(), indices.end(), [&](int a, int b) { return v[a].name

}

Funkcje lambda (C++14)

Argumenty funkcji mogą być typu auto Argumenty w capture list mogą mieć wartości domyślne (przyk. za [3]) – można w ten sposób wymusić przekazanie argumentu przez move semantics (wyjaśnione później) std::unique_ptr ptr(new int(10)); auto lambda = [value = std::move(ptr)] {return *value;}; – można użyć wewnątrz funkcji zmiennych nie widocznych w miejscu wywołania funkcji utworzonych w capture list (typ wyznaczony jak przez auto) auto lambda = [value = 1] {return value;};

Ujednolicona inicjalizacja initializer_list

Ujednolicona składnia inicjalizacji agregatów i zm. skalarnych int tab[] = {1, 2, 3, 4}, x={3}, y{4}; vector< string > = {„one”, „two”, „three”, „four”}; Szablon: initializer_list ma .begin() i .end() i tworzy typ jako który otrzymujemy stałą listę inicjalizacji Elementy listy muszą być typu zgodnego z T_elem, lub dać się do niego skonwertować (bez „narrowing conversion”) initializer_list może być argumentem formalnym funkcji, metody, konstruktora (otrzymamy tzw. initializer list constructor), operatora przypisania void funkcjava(initializer_list list); funkcjava({1.0, 2.0, 3.0}); funkcjava({1.0});

for – nowa składnia

for ( zmienna : zakres) instrukcja; Zakres może być: – Kontener STL, lub inny obiekt z metodami .begin() i .end() – Tablica – string – initializer_list

for – nowa składnia

for ( zmienna : zakres) instrukcja; Przykłady za [1] void f(vector& v) { for (auto x : v) cout << x << '\n'; for (auto& x : v) ++x; // using a reference to allow us to change the value } for (const auto x : { 1,2,3,5,8,13,21,34 }) cout << x << '\n';

Rvalue reference

T&& - referencja do rvalue, może zostać skojarzona jedynie z nienazwaną zmienną tymczasową - główne zastosowanie: do deklaracji konstruktorów przenoszących/operatorów przypisania - gdy wiadomo, że argument konstruktora nie będzie więcej użyty (np. przy przekazywaniu stringów przez wartość), można przenieść jego zawartość zamiast kopiowania -można jawnie określić, żeby argument operatora/konstruktora uznać za rvalue za pomocą szablonu (przykład na kolejnych slajdach): std::move(arg)

Przenoszący konstruktor oraz operator przypisania

Przykłady za [1] template class vector { // ...

vector(const vector&); vector( vector&& ); vector& operator=(const vector&); vector& operator=( vector&& ); // copy constructor // move constructor // copy assignment // move assignment }; // note: move constructor and move assignment takes // non-const && // they can, and usually do, write to their argument

Przenoszący konstruktor oraz operator przypisania

Przykłady za [1] template void swap(T& a, T& b) { T tmp = move (a); a = move (b); b = move (tmp); } // "perfect swap" (almost) // could invalidate a // could invalidate b // could invalidate tmp

constexpr – generalized const expression

Deklarujemy możliwość wyznaczenia podczas kompilacji – Dla zmiennych/obiektów: zmienna da się tak wyznaczyć (w przeciwnym razie błąd), jest const, może być przechowywana w tablicach kompilatora a nie pamięci – Dla funkcji: funkcja jest prosta (… pojedynczy return z wyrażeniem, bez dekl. zmiennych), jeżeli argumenty będą znane podczas kompilacji, to wynik funkcji też – Dla konstruktorów: jak funkcja, jeżeli argumenty będą znane podczas kompilacji, to cały obiekt nim utworzony też Przykłady za [1] enum Flags { good=0, fail=1, bad=2, eof=4 }; constexpr int x1 = bad|eof; // ok void f(Flags f3) { constexpr int x2 = bad|f3; // error: can't evaluate at compile time int x3 = bad|f3; // ok }

constexpr – generalized const expression

Przykłady za [1] enum Flags { good=0, fail=1, bad=2, eof=4 }; constexpr int operator|(Flags f1, Flags f2) { return Flags(int(f1)|int(f2)); } void f(Flags x) { switch (x) { case bad: /* ... */ break; case eof: /* ... */ break; case bad|eof: /* ... */ break; default: /* ... */ break; } }

constexpr – generalized const expression

Przykłady za [1] struct Point { int x,y; constexpr Point(int xx, int yy) : x(xx), y(yy) { } }; constexpr Point origo(0,0); constexpr int z = origo.x; constexpr Point a[] = {Point(0,0), Point(1,1), Point(2,2) }; constexpr int x = a[1].x; // x becomes 1

= default = delete

= default – Jawna deklaracja : wygeneruj tę metodę (dotyczy konstruktorów, destruktorów, operatorów przypisania) = delete – Jawna deklaracja : nie generuj tej metody – Jawna deklaracja : nie nie wywołuj (w ten sposób) tej metody nawet, jeżeli się da Przykłady za [2] struct SomeType { SomeType() = default; //The default constructor //is explicitly stated.

SomeType(OtherType value); };

= default = delete

Przykłady za [2] struct NonCopyable { NonCopyable & operator=(const NonCopyable&) = delete; NonCopyable(const NonCopyable&) = delete; NonCopyable() = default; }; struct NoInt { void f(double i); void f(int) = delete; }; struct OnlyDouble { void f(double d); template void f(T) = delete; };

Konstruktory i inne metody generowane automatycznie

Dla metod – Konstruktor kopiujący – Kostruktor przenoszący – Operator przypisania – Operator przeniesienia (przenoszący operator przypisania) – Destruktor Jeżeli jedna z nich będzie zdefiniowana jawnie, lub określona =default, lub =delete to - pozostałe przenoszące nie zostaną wygenerowane automatycznie - pozostałe nie-przenoszące zostaną wygenerowane (własność ta jest deprecated) Konstruktor bezargumentowy będzie generowany automatycznie jeżeli użytkownik nie zdefiniował żadnego konstruktora

Konstruktory i inne metody generowane automatycznie

Przykłady za [1] class X1 { X1& operator=(const X1&) = delete; }; // Disallow copying // This implicitly also disallows moving of X1s. // Copy initialization is allowed, but deprecated. class X2 { X2& operator=(const X2&) = default; }; // This implicitly also disallows moving of X2s. // Copy initialization is allowed, but deprecated. class X3 { X3& operator=(X3&&) = delete; } // Disallow moving // This implicitly also disallows copying of X3s.

Delegowanie konstruktorów

Przykład za [2] class SomeType { int number; public: SomeType(int new_number) : number(new_number) {} SomeType() : SomeType(42) {} };

Dziedziczenie konstruktorów

Przykład za [1] class Derived : public Base { public: using Base::f; // lift Base's f into Derived's scope -- works in C++98 void f(char); // provide a new f void f(int); // prefer this f to Base::f(int) using Base::Base; // ...

}; // lift Base constructors Derived's scope -- C++11 Derived(char); // provide a new constructor Derived(int); // prefer this constructor to Base::Base(int)

Inicjalizacja niestatycznych zmiennych składowych klasy

Przykład za [1] konstukcje równoważne class A { public: int a = 7; }; class A { public: int a; A() : a(7) {} }; jeżeli inicjalizacja następuje na liście inicjalizacyjnej konstruktora oraz przy deklaracji w kasie to lista inicjalizacyjna ma priorytet i jest wykonywana zamiast (a nie po lub przed) inicjalizacją z deklaracji

Deklaracja nadpisywania

Można zadeklarować, że metoda nadpisuje inną, jeżeli się pomylimy i nie będzie nadpisywała to kompilator zgłosi błąd Przykład za [2] struct Base { virtual void some_func(float); }; struct Derived : Base { virtual void some_func(int) override ; // ill-formed because it doesn't override a base class method };

final

Za pomocą final deklarujemy – że metody już nie będzie wolno nadpisać – że klasy nie będzie wolno odziedziczyć – (wcześniej używano do tego celu dziedziczenia i sekcji private (niedoskonałe)) Przykład za [2] struct Base1 final { }; struct Derived1 : Base1 { }; //ill-formed because the class Base1 has been marked final struct Base2 { virtual void f() final ; }; struct Derived2 : Base2 { void f(); // ill-formed because the virtual function Base2::f has been marked final };

Literały dla własnych typów

Literal operator - nowy operator przeciążony Przykład za [1] Bignum operator"" x (const char* p) { return Bignum(p); } void f(Bignum); f(1234567890123456789012345678901234567890 x ); Argument operatora może być przekazany jako albo łańcuch (tzw. raw literals), albo jako wartość jednego z kilku typów (cooked), możliwości: – całkowitoliczbowy: operator pobiera unsigned long long lub (jak w przykładzie powyżej) const char* argument.

– zmiennopozycyjny: operator pobiera long double lub const char* – łańcuchowy: 2 argumenty operatora (const char*, size_t), możliwe użycie różnych typów znakowych – Literał znakowy: operator pobiera argument typu char

Literały dla własnych typów

Przykłady za [2] OutputType operator "" _suffix(unsigned long long); OutputType operator "" _suffix(long double); … OutputType some_variable = 1234_suffix; // uses the first function OutputType another_variable = 3.1416_suffix; // uses the second function OutputType operator "" _suffix(const char * string_values, size_t num_chars); OutputType operator "" _suffix(const wchar_t * string_values, size_t num_chars); OutputType operator "" _suffix(const char16_t * string_values, size_t num_chars); OutputType operator "" _suffix(const char32_t * string_values, size_t num_chars); … OutputType some_variable = "1234"_suffix; //Calls the const char * version OutputType some_variable = u8"1234"_suffix; //Calls the const char * version OutputType some_variable = L"1234"_suffix; //Calls the const wchar_t * version OutputType some_variable = u"1234"_suffix; //Calls the const char16_t * version OutputType some_variable = U"1234"_suffix; //Calls the const char32_t * version

Template alias

Dla szablonów, zamiast typedef, wygodniejsza składnia Przykład za [1] template using Vec = std::vector>; // standard vector using my allocator Vec fib = { 1, 2, 3, 5, 8, 13 }; // allocates elements using My_alloc vector> verbose = fib; // verbose and fib are of the same type

Szablony ze zmienną liczbą argumentów

Można tworzyć szablony ze zmienną liczbą argumentów (tzw. variadic templates) – szablony funkcji i klas Prosty przykład za [5], zagadnienie obszerne - szczegóły, inne sposoby dostępu do listy argumentów - patrz [5] template void print_comma_separated_list(T value) { std::cout< void print_comma_separated_list(First first,Rest ... rest) { std::cout<

Inne przegląd

vector<> - ok. W C++11 extern template class std::vector; - optymalizacja kompilacji: nie rozwijaj szablonu w tym obj, będzie rozwinięty w innym module nullptr - stała kompilatora zamiast zamiast NULL z preprocesora static_assert (warunek, ”Komunikat błędu”) - asercje czasu kompilacji void f [[ noreturn ]] (); // f() will never return - atrybuty – zamiast #pragma, w standardzie: noreturn i carries_dependency noexcept – rozbudowany mechanizm ograniczenia propagacji wyjątków, jeżeli funkcja zadeklarowana jako noexcept wyrzuci wyjątek, to program zostanie zakończony (terminate()) explicit – modyfikator, który dla operatora konwersji spowoduje, że operator nie będzie użyty w konwersjach niejawnych (od C++98 explicit przy konstruktorze znaczyło, że nie może on być uzyty jako niejawne operatory konwersji)

Inne przegląd

alignas - przykłady za [1] alignas(double) alignas(16) unsigned char c[1024]; char[100]; // array of characters, suitably // aligned for doubles // align on 16 byte boundary enum class Kolor:char (Czerwony, Zielony); - „Strong enum” jawny typ bazowy (musi być całkowity) - wartości tylko w zakresie enumeracji Kolor::Czerwony Nieco inne traktowanie unii (więcej wolno) POD (Plain Old Data) – struktura jak z C, dozwolony konstruktor, 2 warianty long long – min. 64 bity, literały np. 10LL Literały Unicode u8 "I'm a UTF-8 string.” u "UTF-16 string." oraz typy dla szerokich znaków char16_t i char32_t U "UTF-32 string.„ Raw string literals R”( C:\temp\ )” … … … R”xxx( coś)” )xxx”

Inne – C++14

Binary literals – NARESZCIE!

0B0000010 0b0000010 Literały dla std::basic_string ”tekst”s i jednostek czasu z std::chrono::duration Digit separators – dozwolony apostrof dla poprawy czytelności (lub wręcz przeciwnie) małych i dużych liczb (przykład za [2]) auto integer_literal = 1'000'000; auto floating_point_literal = 0.000'015'3; auto binary_literal = 0b0100'1100'0110; auto silly_example = 1'0'0'000'00; [[deprecated]] – atrybut ostrzeżenia o możliwości przyszłego wycofania jego argumentu [[deprecated]] int f(); [[deprecated("g() is thread-unsafe. Use h() instead")]] Szablony dla typów zmiennych (wcześniej: funkcji, klas) Zniesione ograniczenia dla constexpr … … …

Inne – duże nowości C++11

Współbieżność, wątki, synchronizacja, dzielona pamięć … Rozszerzenia i ulepszenia biblioteki standardowej (wiele …)

Wielki nieobecny C++11

Koncepty

– Miały służyć do definiowania wymagań (ograniczeń) dla argumentów wzorca (np. „argument nie powinien być wskaźnikiem”) – Nie weszły do standardu, gdyż opóźniłyby i tak spóźnione jego wprowadzenie – Pojawią się w następnym standardzie

Dziękuję za uwagę