![>](style_images/1/nav_m.gif)
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.119.166.31] |
![]() |
|
Страницы: (78) [1] 2 3 ... 77 78 ( Перейти к последнему сообщению ) |
![]() |
![]() |
|
К октябрю 2007 года комитет запланировал публикацию законченного черновика стандарта C++09(который будет доступен публике для рассмотрения и критики).
В октябре 2008 комитет внесёт окончательные коррективы в стандарт и, наконец, на 2009 год запланированна публикация нового стандарта "ISO/IEC 14883(2009): Programming Language C++". Надеюсь, не мне одному интересно, что же ожидает нас в новом С++. Потому, проштудировав документы, доступные обычным смертным на сайте open-std.org, я сделал маленький обзорчик ожидаемых вкусностей, которые готовит нам новый стандарт. Итак, в кратце, крупные нововведения следующие: Примечание: имеется так же обзор того, чего НЕ будет в С++09. Новый стандарт C++: C++09 (сообщение #1602819) Rvalue References Появились т.н. ссылки на rvalue. Сначала поясню зачем их вообще изобрели. Исходных проблемы было две: forwarding problem и move semantics. Forwarding problem Эта проблема заключается в том, что текущий стандарт, для заданного выражения E(a1,a2,...,aN), которое зависит от параметров a1,a2,...,aN, не позволяет написать такую ф-цию(или функтор), которая будет эквивалентна этому выражению. Проблема актуальна для разного рода шаблонных обёрток, фабрик, обобщённых функторов и т.п. За идеал перенаправляющей ф-ции(perfect fowarding function) f(a1,a2,...,aN), которая вызывает g(a1,a2,...,aN) взяли следующие критерии: Вот простейший пример: ![]() ![]() template < class T1, class T2, class T3> void f(T1 &a1, T2 &a2, T3 &a3) { g(a1, a2, a3); } ![]() ![]() template < class T1, class T2, class T3> void f(const T1 &a1, const T2 &a2, const T3 &a3) { g(a1, a2, a3); } ![]() ![]() template<class A1> void f(A1 & a1) { g(a1); } template<class A1> void f(A1 const & a1) { g(a1); } Короче говоря, текущий стандарт решить эту проблему не позволяет. Move semantics С++ - язык, построенный на семантике копирования(copy semantics). Что такое семантика перемещения(move semantics)? Хороший пример - std::auto_ptr. Его конструктор копирования берёт неконстантную ссылку и перемещает хранимую в исходном объекте сущность в новый объект(тем самым избегая глубокого копирования). Но, несмотря на то, что конструктор копирования auto_ptr берёт неконстантную ссылку, его суть не в том, чтобы изменить объект, а в том, чтобы переместить к себе его содержимое. Так же, семантика перемещения не помешала бы строкам. Вообразим, что строки у нас без подсчёта ссылок. Теперь вообразим, сколько ресурсов будет затрачено на вычисление такого выражения: ![]() ![]() string s = string("123")+"234"+"567"+"678"+"789"; будет создано как минимум 5 временных объектов и потом ещё произойдёт глубокое копирование результирующей строки в s(если нету подсчёта ссылок). А теперь, вообразим, как было бы прекрасно, если бы конструктор копирования умел бы отличать какой объект ему подсунули - временный или нет. Действительно, о временных объектах можно не волноваться и с чистой совестью "забирать" у них выделеный ими буфер, без необходимости глубокого копирования. К слову, эту проблему можно решить текущими возможностями языка, но очень уж некрасиво... Что же нам предлагает новый стандарт? А предлагает он следующее: ввести новый тип ссылок - rvalue reference. Синтаксис: ![]() ![]() T t; // lvalue T &rt = t; // lvalue reference T &&rrt = t; // rvalue reference // правила сворачивания ссылок T cv1 & cv2 & <=> T cv12 & T cv1 & cv2 && <=> T cv12 & T cv1 && cv2 & <=> T cv12 & T cv1 && cv2 && <=> T cv12 && Любая именованная rvalue-ссылка трактуется как lvalue. Любая неименованная rvalue-ссылка трактуется как rvalue. Т.к. теперь появилась возможность различать тип выражения(lvalue или rvalue), появилась и возможность кастовать lvalue к rvalue: static_cast<T &&>(lval) будет трактоваться как rvalue. Возвращаемое значение из ф-ции интерпретируется как rvalue т. е. ![]() ![]() return val; <=> return static_cast<ret_T &&>(val); Т.о. можно избежать глубокого копирования и ограничиться только лишь перемещением из возвращающей ф-ции в вызвавшую(при наличии соответствующего конструктора). Forwarding problem решается теперь следующим образом: ![]() ![]() void g(long & a1) { ++a1; } template<class A1> void f(A1 && a1) { g(static_cast<A1 &&>(a1)); } int i = 5; g(i); //fails - int & to long & - запрещённый каст ещё в C++03 f(i); //fails // A1 выводится(deduced) как int & // A1 && <=> int & && <=> int & // a1 - lvalue-reference of int // static_cast<int &>(a1) - lvalue-reference of int // f(i) не компилируется по тем же причинам, что и не компилируется g(i) g(1L); // fails - rvalue of long to long & - запрещённый каст ещё в C++03 f(1L); // fails // A1 выводится как long // a1 - lvalue of long(named rvalue-reference <=> lvalue) // static_cast<long &&>(a1) - rvalue of long(lvalue to rvalue cast) // f(1L) не компилируется т.к. rvalue to non-const lvalue-reference - запрещённый каст ещё в C++03 long L; g(L); // ok f(L); // ok // A1 выводится как long & // A1 && <=> long & && <=> long & // a1 - lvalue-reference of long // static_cast<long &>(a1) - lvalue-reference of long // f(L) компилируется(как и должна) Move semantics обеспечивается следующим образом: ![]() ![]() class string { public: string(const string &); // copy constructor string(string &&); // move constructor string &operator +=(string &); // copy semantics operator += string &&operator +=(string &&); // move semantics opertor += }; string &&operator +(string &&s1, string &&s2) { return s1 += s2; // т.к. s1 - временный объект, мы не создаём новую строку, мы модифицируем существующую } В случаях, когда необходимо вызвать конструктор перемещения для объекта, который не является rvalue, можно сделать каст, запросив необходимое поведение следующим образом: ![]() ![]() string s1("abc"); string s2 = s1; // construct s2 as s1 copy string s3 = static_cast<string &&>(s1); // move from s1 to s2 Лично я эту фичу считаю очень полезной. Особенно, учитывая появившуюся возможность отличать временные объекты от невременных таким образом увеличив производительность в разы, избавившись от лишних операций копирования. Template aliases Думаю все оценят эту фичу. Проблем, побудивших ввести алиасы две. Первая заключается в том, что очень часто появляется нужда в "шаблонном typedef'е". Wrokaround'ом этой проблемы как правило является следующая конструкция: ![]() ![]() template < class T > struct MyVector { typedef std::vector< T, MyAllocator< T > > type; }; MyVector< int >::type vec; // не очень красивая запись Вторая же проблема выражается в том, что при использовании вышеобозначенного workaround'а перестаёт работать вывод шаблонных параметров. ![]() ![]() template < class T > void f(std::vector< T > &) { } template < class T > void f2(typename MyVector< T >::type &) { } std::vector< int > v; MyVector< int >::type v2; f(v); // ok f2(v2); // ill-formed Алиасы позволяют решить обе проблемы. Алиасы представляют из себя объявления. Они не определяют новых типов. ![]() ![]() template < class T > using MyVector = std::vector< T, MyAllocator< T > >; using MyFloat = float; void f1(float) { } // ok void f1(MyFloat) { } // ill-formed - redefinition Шаблонные алиасы нельзя специализировать, но, можно специализировать тип синонимом которого является алиас. Variadic templates Это нововведение избавляет программиста, реализующего библиотеку списков типов или библиотеку, подобную boost::bind от реализации всех возможных вариаций типа ![]() ![]() template < class R > unspecified bind(...); template < class R, class A1 > unspecified bind(...); template < class R, class A1, class A2 > unspecified bind(...); // ... template < class R, class A1, class A2, ..., class AN > unspecified bind(...); И позволяет сделать шаблон, принимающий переменное количество шаблонных параметров: ![]() ![]() template < class R, class... Args> // здесь троеточие - это синтаксический элемент R f(Args... args) { return g(args...); // вызываем g, передавая ей все аргументы. } Как к типам(Args), так и к экземплярам этих типов(args) можно применять разные операторы. ![]() ![]() template < class R, class... Args > R fwd_by_pointer(Args &... args) // <=> R fwd_by_pointer(Arg1 & arg1, Arg2 & arg2, ..., ArgN & argN) { return g(&args...); // <=> return g(&arg1, &arg2, ..., &argN); } Количество типов в наборе можно узнать с помощью нового оператора sizeof...: ![]() ![]() template < class... Types > struct S { enum { result = sizeof...(Types) }; }; Языковых средств для вытягивания типов из набора(Args) нету, но, это не очень сложно делается руками(и уже сделано в стандартной библиотеке - std::tuple и иже с ним). Языковых средств для вытягивания значения из набора(args) вроде как нету, но, опять же, руками это делается несложно - std::tuple тому пример. В документах встречалось упоминание, что значение можно вытянуть как из массива(args[3], к примеру), но в грамматике я такого упоминания не нашел. Concepts Ну это вообще просто сказка ![]() Пару слов про сами концепции. Любой, кто использовал обобщённые алгоритмы/структуры данных сталкивался с разного рода требованиями к обобщаемому типу. Наиболее распространенные: DefaultConstructible, CopyConstructible, LessThanComparable. Также, концепциями являются InputIterator, OutputIterator, ForwardIterator, etc. Короче говоря, это требования к обобщаемому типу, невыполнение которых может привести к ошибке инстанцирования шаблона. На данный момент такие требования повсеместно встречаются в документации(IS, boost docs, etc). Теперь эти требования можно будет выражать в коде. Какие проблемы решат концепции? Ну, во-первых, это, конечно то, что теперь, тип будет сначала проверятся на соответствие концепции и только после удачного завершения этой проверки, произойдёт попытка инстанцирования шаблона. Т.о. если тип не LessThanComparable, то при попытке использовать его в контейнере map(к примеру) не придётся втыкать на километры выданных компилятором ошибок. Ошибка будет выглядеть примерно так: "тип T не является LessThanComparable", что, замечу, большой плюс. Все, кто использовал boost::bind/lambda::bind оценят ![]() Во-вторых, для компилируемого на данный момент клиентского кода, увеличится устойчивость к будущим изменениям в коде библиотеки(гарантии на завтрашний день) - если требование раньше было всего лишь словами и только в следующей версии библиотеки эти слова воплотили в жизнь, то без концепций, некоторый клиентский код может перестать работать, чего не скажешь о коде защищенном концепциями - даже если шаблон мог бы быть инстанцирован, но тип не прошёл проверок на соответствие концепциям, то будет выдана ошибка. В-третьих, писать обобщённый код станет проще. С концепциями можно делать что только душе угодно. К примеру, если тип vector не соответствует концепции Stack(у него нету ф-ций push/pop), но, принципиально его можно использовать как тип соответствующий этой концепции(можно использовать ф-ции push_back/pop_back), то, без потери для общности, можно написать что-то вроде адаптера(concept_map), который будет приспосабливать данный тип к заданной концепции. Концепциями можно защитить не весь класс, а только некоторые его методы. Также, можно разработать несколько версий алгоритма эффективных для той или иной концепции и перегрузить его так, что будет выбран наиболее подходящий алгоритм. Синтаксис концепций интуитивно понятен и поясню я только некоторые моменты. ![]() ![]() // вот так определяются концепции auto concept LessThanComparable< typename T > { bool operator<(T, T); }; template< LessThanComparable T > // вот так предъявляются требования к типу T const T& min(const T& x, const T& y) { return x < y ? x : y; } template < typename T > where LessThanComparable< T > // или можно предъявить требования так const T& min(const T& x, const T& y) { return x < y? x : y; } // пример более объёмной концепции auto concept Regular < typename T > { T::T(); // default constructor T::T(const T&); // copy constructor T::~T(); // destructor T& operator=(T&, const T&); // copy assignment bool operator==(T, T); // equality comparison bool operator!=(T, T); // inequality comparison void swap(T&, T&); // swap }; // ещё пример auto concept Convertible <typename T, typename U> { operator U(T); }; template < typename U, typename T > where Convertible< T, U > // концепции можно использовать для задания некоторых взаимоотношений между несколькими типами U convert(const T& t) { return t; } // итератор auto concept InputIterator < typename Iter > { typename value_type; // ассоциированные типы typename reference; typename pointer; typename difference_type; where Regular<Iter>; // вложенные требования where Convertible<reference_type, value_type>; reference operator*(Iter); // dereference Iter& operator++(Iter&); // pre-increment Iter operator++(Iter&, int); // post-increment // ... }; template <InputIterator Iter> where Regular<Iter::value_type> Iter find(Iter first, Iter last, const Iter::value_type& value) { while (first != last && *first != value) ++first; return first; } auto concept BinaryFunction<typename F, typename T1, typename T2> { typename result_type; result_type operator()(F&, T1, T2); }; auto concept BinaryPredicate<typename F, typename T1, typename T2> : BinaryFunction<F, T1, T2> // пример "наследования" концепций { where Convertible<result_type, bool>; }; // уточнение для char * у которого нету ассоциированных с ним типов // аналог traits, только намного более мощный(см. далее) concept_map InputIterator<char*> { typedef char value_type ; typedef char& reference ; typedef char* pointer ; typedef std:: ptrdiff_t difference_type ; }; concept Stack<typename X> { typename value_type; void push(X&, value type); void pop(X&); value type top(const X&); bool empty(const X&); }; // пример адаптации вектора к концепции Stack template<typename T> concept_map Stack< std::vector<T> > { typedef T value_type; void push(std:: vector<T>& v, T x) { v. push_back(x); } void pop(std:: vector<T>& v) { v. pop_back(); } T top(const std:: vector<T>& v) { return v.back(); } bool empty(const std::vector<T>& v) { return v.empty(); } }; // концепция, которой удовлетворяет вектор(и не только) concept BackInsertionSequence<typename X> { typename value_type = X::value type; void X::push_back(value type); void X::pop_back(); value_type& X::back(); const value_type& X::back() const; bool X::empty() const; }; // пример, как можно адаптировать любой тип, удовлетворяющий концепции C1, к концепции C2. // другими словами, как адаптировать одну концепцию к другой template<BackInsertionSequence X> concept_map Stack<X> { typedef X::value_type value_type; void push(X& x, value_type value ) { x. push_back(value); } void pop(X& x) { x. pop_back(); } T top(const X& x) { return x.back(); } bool empty(const X& x) { return x.empty(); } }; // пример перегрузки на основе концепций - будет выбрана самая "специфичная" форма //т.е. для BidirectionalIterator будет выбран второй вариант, несмотря на то, что удовлетворяет и первый(InputIterator) template<InputIterator Iter> void advance(Iter& x, Iter::difference type n) { while (n > 0) { ++x; --n; } } template<BidirectionalIterator Iter> void advance(Iter& x, Iter::difference type n) { if (n > 0) while (n > 0) { ++x; --n; } else while (n < 0) { --x; ++n; } } template<RandomAccessIterator Iter> void advance(Iter& x, Iter::difference type n) { x += n; } // пример разных реализаций контейнера для разных хранимых типов данных template<EqualityComparable T> class dictionary { // slow, linked-list implementation }; template<LessThanComparable T> where !Hashable<T> class dictionary<T> { // balanced binary tree implementation }; template<Hashable T> class dictionary<T> { // hash table implementation }; // пример, как можно обложить ограничениями не весь класс, а только некоторые ф-ции(причём разными ограничениями) template<typename T, typename U> struct pair { where DefaultConstructible<T> && DefaultConstructible<U> pair() : first(), second() { } where CopyConstructible<T> && CopyConstructible<U> pair(const T& t, const U& u) : first(t), second(u) { } where Destructible<T> && Destructible<U> ~pair() { } where Assignable<T> && Assignable<U> pair& operator=(const pair<T, U>& other) { first = other.first; second = other.second; } T first; U second; }; // ещё пример, как помимо CopyConstructible, может понадобится DefaultConstructible // но вектор может ф-ционировать и без второго требования потому его(требование) относят только к отдельной ф-ции. template<CopyConstructible T> class vector { public: // обратите внимание, как одна ф-ция разделилась на две, дабы добавить контейнеру общности :) // vector(size t n, const T& value = T()); vector(size t n, const T& value); where DefaultConstructible<T> vector(size t n); }; Ключевое слово where в последней версии вроде как решили заменить на слово requires. Unicode characters/strings Ну, собственно, ничего интересного, кроме самого факта: теперь в С++ оффициальная поддержка UTF-16(u"...") и UTF-32(U"..."). Ну а факт, я считаю, немаловажный и вполне достойный соответствующего внимания со стороны публики ![]() Появились новые типы char16_t и char32_t. Также, отдельно рассматривается добавление UTF-8(E"..."). Initializer lists ![]() ![]() X t1 = v; // "copy initialization" possibly copy construction X t2(v); // direct initialization X t3 = { v }; // initialize using initializer list <<<================= X t4 = X(v); // make an X from v and copy it to t4 Достаточно обширное нововведение. Пока всех подробностей не выяснил, но, в двух словах постараюсь рассказать. В языках типа C# практикуется такое: ![]() ![]() f(new char[] {'1', 'a', '-'}); В С++09 предполагается нечто подобное(только без new ![]() Теперь можно будет написать ![]() ![]() std::vector< int > v = { 1, 2, 3, 4, 5 }; Как написать класс, чтобы его можно было вот так инициализировать? ![]() ![]() #include <initializer_list> // этот хэдер предоставляет класс std::initializer_list namespace std { template<class E> class initializer_list { // representation implementation defined // (probably two pointers or a pointer and a size) // implementation defined constructor public: // default copy construction and copy assignment // no default constructor // default trivial destructor constexpr int size() const; // number of elements const T* begin() const; // first element const T* end() const; // one-past-the-last element }; } class A { public: A(std::initializer_list< char > a) { /* ... */ } // ... }; class B { public: B(std::initializer_list< double > a) { /* ... */ } // ... }; void f(const A &a); // #1 void f(const B &b); // #2 int main { A a1 = {1, 2, 3}; A a2{2, 3, 4}; A a3; a3 = A{3, 4, 5}; f({1, 2., 3}); // ambiguity f(A{1, 2., 3}); // #1 f(B{1, 2., 3}); // #2 f({1., 2., 3.}); // #2 f{'a', 'b', 'c'}; // #1 return 0; } Синтаксические мелочи static_assert Новое ключевое слово, позволяет во время компиляции сделать проверку и, в случае чего, сгенерить ошибку компиляции(текст ошибки можно указывать). Наибольшее применение, имхо, будет иметь в шаблонах, хотя, с появлением концепций - сомнительно ![]() Так же, возможно использование как замена старой доброй директивы #error. ![]() ![]() template <typename T> struct Check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; Расширенная функциональность sizeof ![]() ![]() struct C { some_type m; // ... }; const std::size_t sz = sizeof(C::m); // C++03 - error, C++09 - ok Delegating Constructors ![]() ![]() // C++03 workaround class A { void Init(/* ... */) { /* ... */ } public: A() { Init(); }; A(/* ... */) { Init(); /* ... */ } }; // C++09 well-formed code class A { public: A() { /* initializations */ }; A(/* ... */) : A() // <<==== delegating construction { /* other initializations */ } }; Inheriting Constructors ![]() ![]() struct B1 { B1( int, int ) {} }; struct B2 { B2( double, double ) {} }; struct D1 : B1 { using B1::B1; // impliclty declare D1( int a1, int a2 ) : B1(a1, a2) {} int x; }; struct D2 : B2 { using B2::B2; // impliclty declare D2( double a1, double a2 ) : B2(a1, a2) {} B1 b; }; Deducing the type of variable from its initializer expression. Достаточно интересная штука... настолько же, насколько и опасная, имхо... ![]() ![]() int foo(); auto x1 = foo(); // x1 : int const auto& x2 = foo(); // x2 : const int& auto& x3 = foo(); // x3 : int&: error, cannot bind a reference to a temporary float& bar(); auto y1 = bar(); // y1 : float const auto& y2 = bar(); // y2 : const float& auto& y3 = bar(); // y3 : float& A* fii(); auto* z1 = fii(); // z1 : A* auto z2 = fii(); // z2 : A* auto* z3 = bar(); // error, bar does not return a pointer type // из очень полезных применений вижу следующее // особенно полезно при замене контейнера(-ов) на концептуально аналогичные, но по типизации разные std::map< std::string, std::map< std::string, std::set< std::vector< bool > > > > container; for (auto i1 = container.begin(), e1 = container.end(); i1 != e1; ++i1) for (auto i2 = i1->second.begin(), e2 = i1->second.end(); i2 != e2; ++i2) for (auto i3 = i2->second.begin(), e3 = i2->second.end(); i3 != e3; ++i3) for(auto i4 = i3->begin(), e4 = i3->end(); i4 != e4; ++i4) { /* ... */ } // ещё, появилась штука, ожидаемая под названием typeof. // в C++09 её назвали decltype decltype(container.begin()) i = container.begin(); Extended friend Declarations ![]() ![]() class C; typedef C Ct; class X1 { friend C; // OK: class C is a friend }; class X2 { friend Ct; // OK: class C is a friend }; class X3 { friend class Ct; // C++09 - ok, C++03 - ill-formed }; Extern templates ![]() ![]() template < class T > class MyVector { /* ... */ }; template class MyVector< int >; // explicit instantination extern tempalte class MyVector< int >; // extern explicit instantination сделано для того, чтобы диначическая библиотека могла сделать у себя explicit instantination, а клиент у себя extern explicit instantintaion Right Angle Brackets ![]() ![]() std::vector<std::set<int>> v; // C++03 - ill-formed, C++09 - well-formed Range-based for-loop ![]() ![]() int array[5] = { 1,2,3,4,5 }; std::vector< int > vec = { 1, 2, 3, 4, 5 }; // так инициализировать нельзя, но мы это опустим :) for ( auto& x : array ) x *= 2; for ( float x : vec ) std::cout << x << std::endl; C99 Compatibility: __func__ ![]() ![]() namespace N { void f(); } void N::f() { } // __func__ is "f" struct S { S() : s(__func__) { } // ok, s points to "S" ~S() { } // __func__ is "~S" operator int() { } // __func__ is "conversion operator" template<class T> int g(); const char *s; }; S operator +(S,S) { } // __func__ is "operator+" template<> int S::g<int>() { } // __func__ is "g" struct S { S() : s(__func__) { } // ok const char *s; }; void f(const char * s = __func__); // error: __func__ is undeclared Generalized Constant Expressions constexpr - новое ключевое слово. Суть нововведения в том, что теперь, например, можно как размерность массива использовать результат, возвращенный ф-цией. ![]() ![]() struct A { constexpr A(int i) : val(i) { } constexpr operator int() { return val; } constexpr operator long() { return 43; } private: int val; }; template<int> struct X { }; constexpr A a = 42; X<a> x; // OK: unique conversion to int int ary[a]; // error: ambiguous conversion Explicit Conversion Operators ![]() ![]() class T { }; class X { public: explicit operator T() const; }; int main() { X x; // Direct initialization: T t4( x ); // Copy initialization: T t8 = x; // error // Cast notation: T t12 = (T) x; // Static_cast: T t16 = static_cast<T>( x ); // Function-style cast: T t20 = T( x ); return 0; } Raw String Literals ![]() ![]() char *s1 = "('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|"; char *s2 = R"[('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|]" // кто работал с regex на с++ - оценят :) // post: strcmp(s1, s2) == 0 char *s3 = "<HTML>\n" "<HEAD>\n" "<TITLE>Auto-generated html formated source</TITLE>\n" "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=windows-1252\">\n" "</HEAD>\n" "<BODY LINK=\"#0000ff\" VLINK=\"#800080\" BGCOLOR=\"#ffffff\">\n" "<P> </P>\n" "<PRE>\n"; char *s4 = R"[\ <HTML> <HEAD> <TITLE>Auto-generated html formated source</TITLE> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> </HEAD> <BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff"> <P> </P> <PRE> ]" // post: strcmp(s3, s4) == 0 A name for the null pointer: nullptr ![]() ![]() char* ch = nullptr; // ch has the null pointer value char* ch2 = 0; // ch2 has the null pointer value int n = nullptr; // error int n2 = 0; // n2 is zero if( ch == 0 ); // evaluates to true if( ch == nullptr ); // evaluates to true if( ch ); // evaluates to false if( n2 == 0 ); // evaluates to true if( n2 == nullptr ); // error if( nullptr ); // error, no conversion to bool if( nullptr == 0 ); // error // arithmetic nullptr = 0; // error, nullptr is not an lvalue nullptr + 2; // error Alignment Support ![]() ![]() // новые ключевые слова: alignas, alignof const std::size_t align_of_int = alignof(int); T alignas(T) alignas(long) t1; T alignas(T) alignas(align_of_int) t2; Prohibited access specifier ![]() ![]() template< typename T > struct owned_ptr { public: explicit owned_ptr( T * p ) : pt( p ) {} ~owned_ptr() { delete pt; } T * operator->() { return pt; } T const * operator->() const { return pt; } private: T * pt; void foo(); prohibited: owned_ptr( owned_ptr const & ); owned_ptr & operator=( owned_ptr const & ); }; template< typename T > void S< T >::foo() { new owned_ptr(*this); // compile-time error(не link-time) } Explicit class and default definitions ![]() ![]() class A explicit { // no implicitly declared/defined special member functions(default ctor, copy ctor, copy assignment operator, destructor) }; class B explicit { public: B() {default} // default ctor definition(compiler generated) }; class I explicit { public: virtual ~I() {default} }; Defaulted and Deleted Functions ![]() ![]() struct type { type() = default; // trivial virtual ~type() = default; // non-trivial because virtual type & operator =( const type & ); // declaration and.... }; inline // the inline definition keeps it trivial type & type::operator =( const type & ) = default; // -------------------------------------------------------------- struct type { type( const type & ); // declaration and.... }; type::type() = default; // the non-inline makes it non-trivial // -------------------------------------------------------------- struct type { type & operator =( const type & ) = delete; type( const type & ) = delete; type() = default; }; // -------------------------------------------------------------- struct type { void * operator new( std::size_t ) = delete; }; // -------------------------------------------------------------- struct type { ~type() = delete; // disable destructor }; // -------------------------------------------------------------- struct type { type( long long ); // can initialize with an long long type( long ) = delete; // but not anything less }; extern void bar( type, long long ); // and the same for bad overloads void bar( type, long ) = delete; // of free functions // -------------------------------------------------------------- struct type { type( long long ); explicit type( long ) = delete; }; extern void function( type ); function( type( 42 ) ); // error 42 promotes to long function( 42 ); // okay type(long long); type(long) not considered Pure implementation method declaration ![]() ![]() struct Base { virtual void f1() = 0; virtual void f2() = 0; }; struct S : public Base { virtual void f1() > 0; // должно быть определение S::f1, иначе compile-time error virtual void f2() >= 0; // определение S::f2 может быть, а может и не быть :) virtual void f3() > 0; // compile-time error - нету объявления Base::f3. virtual void f4() >= 0; // compile-time error - нету объявления Base::f3. }; Strongly Typed Enums ![]() ![]() enum class E { E1, E2, E3 = 100, E4 /* = 101 */ }; void f( E e ) { if( e >= 100 ) ; // error: no E to int conversion } int i = E::E2; // error: no E to int conversion // ------------------------------------------------------ enum class E { E1, E2, E3 = 100, E4 /* = 101 */ }; E e1 = E1; // error E e2 = E::E2; // ok // ------------------------------------------------------ enum class E : unsigned long { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; unsigned long ul = E::Ebig; Формальные мелочи Conditionally-Supported Behavior Добавлен новый вид определяемого стандартом поведения. Теперь, конструкции для которых поведение было неопределено(UB), считаются conditionally-supported и могут интерпретировать либо как implementation-defined, либо как ill-formed. Замена некоторых UB на Diagnosable Errors К примеру, теперь передача non-POD в элипсис приведёт к ошибке компиляции, а не к UB как в C++03. Новая модель выполнения программы В связи с добавлением оффициальной поддержки multithreading. Убрали понятие sequence point. Добавили понятие evaluation - набор выборок(чтение значения переменной) и side effects, которые происходят в ходе вычисления выражения. Понятие sequence point заменили аж тремя понятиями sequencing constraints: sequenced before, unsequenced, indeterminately sequenced. Эти понятия - отношения между двумя evaluations A и B(A sequenced before B, A and B unsequenced, etc). Теперь порядок вычисления операндов не unspecified. Теперь evaluations of operands unsequenced ![]() В связи с отсутствием понятия sequence point вычисление операндов операторов "a && b", "a || b", "a ? b : c" и "a, b" примерно следующее: evaluation of a is sequenced before evaluation of b. Облегчение ограничений на POD'ы Ввели два новых понятия: trivial-class и standard-layout-class. POD - это класс, который является одновременно и trivial и standard-layout. Теперь все гарантии, которые давал POD можно разделить на 2 части: первые даются для trivial типов, вторые для standard-layout. В общем теперь большее кол-во типов будет поддаваться копированию посредством memcpy и т.п. Перегрузка операторов Рассматривается возможность перегрузки операторов . .* static_cast<> const_cast<> dynamic_cast<> Стандартная библиотека Дополнение к этому пункту и/или более подробные описания фич можно увидеть здесь: Новый стандарт C++. C++09 (сообщение #1601275) cstdint из C99 Добавленна опциональная поддержка типов с размером точно соответствующим указанному. int8_t, int16_t, int32_t, int64_t uint8_t, uint16_t, uint32_t, uint64_t Обязательная поддержка для типов с размером не меньшим указанного. int_least8_t, int_least16_t, etc. uint_least8_t, etc. И обязательная поддержка типов с размером точно соответствующим указанному, но, возможно, более быстрых, чем их least-эквиваленты. int_fast8_t, etc. uint_fast8_t, etc. Контейнеры Sequence container: std::array - см. ниже "Перешло из boost". Unordered associative containers: unordered_map, unordered_multimap, unordered_set, unordered_multiset Вполне ожидаемые контейнеры работающие по принципу хэширования, ранее известные под названием hash_set/map в "вольных" реализациях STLport, MS VC-8.0. В связи с введением rvalue-reference и move semantics Во-первых, повсеместное добавление/переведение вышеобозначенной семантики для повышения производительности(там, где это возможно). Добавлены ф-ции помошники move и forward, означающие не что иное, как прямое предназначение rvalue-ссылок. Первая вынуждает использовать семантику перемещения даже если операнд - lvalue(-reference). Вторая осуществляет необходимые телодвижения для достижения perfect forwarding ![]() Добавлен move_iterator< class Iter >, который работает точно также, как Iter, за исключением того, что его dereferencing оператор принуждает использовать семантику перемещения. В связи с введением constexpr Много где встречаются эти константные ф-ции(результат которых можно использовать даже для определения статического массива). К примеру numeric_limits. Теперь его ф-ции min/max отвечают соответствующим требованиям. Перешло из boost std::tuple Тож самое, что и boost::tuple, за одним отличием - переведён на синтаксис variadic templates. std::bind Тож самое, что и boost::bind, за одним отличием - переведён на синтаксис variadic templates. std::array(sequence container) Тож самое, что и boost::array. Вроде где-то упоминалось, что собираются сделать его N-мерным(в отличие от 1-мерного boost::array). std::regex См. boost::regex Многопоточность Ничего не могу сказать более определённого, чем то, что в новом С++ будет поддержка многопоточности и будет предоставленно API, совместимое с posix pthreads. Также, возможность выполнения атомарных операций(необходимо для синхронизации - реализации спин-локов) P.S. Обсуждение упомянутых фич, а также, изложение информации о неупомянутых фичах очень даже приветствуется ![]() remark: Добавлена ссылка на пост с продолжением: Новый стандарт C++. C++09 (сообщение #1601275)(стандартная библиотека) remark: Добавлена ссылка на пост с продолжением: Новый стандарт C++: C++09 (сообщение #1602819)(чего не будет в С++09) remark: Добавлена ссылка на пост с продолжением: TR1. Technical Report on C++ Library Extensions. remark: Добавлена ссылка на пост с продолжением: Trip Report: February/March 2008 ISO C++ Standards Meeting |
Сообщ.
#2
,
|
|
|
Целая статья - огромная работа!
![]() Щас буду вникать. Спасибо за перевод. ![]() |
Сообщ.
#3
,
|
|
|
С одной стороны эти изменения полезны, с другой усложняют и без того непростой язык. Отсюда следует, что должен появиться более простой язык, который станет самым популярным. Это не Java и не C#. Что касается описанных изменений, мне больше всего понравились Delegating Constructors.
|
Сообщ.
#4
,
|
|
|
Цитата prografix @ С одной стороны эти изменения полезны, с другой усложняют и без того непростой язык. Чуешь зависимость: "Язык усложняется - его использование упрощается"? Цитата Отсюда следует, что должен появиться более простой язык, который станет самым популярным. Silver bull |
![]() |
Сообщ.
#5
,
|
|
prografix, имхо здесь из усложнений только rvalue-references и то из-за того, что мозг ещё не освоился со всеми ньансами и не совсем понятно чего когда будет происходить... Но это только вопрос времени и мозг освоится так же быстро, как это было когда "появились" lvalue-references(в Си их же не было)... Всё остальное только упрощает код. Взять те же variadic templates. Если сейчас, я как огня боюсь залезать в бустовые хэдеры и пытаться одуплить реализацию bind, то, потом(с введением соответствующей фичи) - это будет так же просто, как одуплить реализацию вектора. В proposal'е была даже "примерная" реализация bind и, как ни странно, я всё понял
![]() Цитата Hryak @ эт че? Silver bullshitet ? Добавлено psx, да ты не торопись - разберись для начала с текущей реализацией языка ![]() |
Сообщ.
#6
,
|
|
|
Кстати, есть небольшая презенташка о состоянии готовящегося стандарта. Там описаны многие новые фичи языка.
Добавлено Еще также подумывают про введение сборки мусора и облегчение создания динамически загружаемых модулей. |
![]() |
Сообщ.
#7
,
|
|
mo3r, сборка мусора будет, но ничего, кроме самого факта я к сожалению не нашёл.
Модулей в C++09 не будет. Скорее всего сделают отдельным TR. А может и до следующего стандарта отложат. |
Сообщ.
#8
,
|
|
|
archimed7592 а чего, boost стандартом так и не станет пока?
|
Сообщ.
#9
,
|
|
|
archimed7592, Спасибо за статью! Очень понравилось.
Только вот не понял две вещи: ![]() ![]() 1. Raw String Literals 2. Prohibited access specifier Обьясните поподробнее плз... Добавлено Ув. Модераторы, Может прибьете к потолку? |
![]() |
Сообщ.
#10
,
|
|
Цитата Xenon_Sk @ archimed7592 а чего, boost стандартом так и не станет пока? Весь - нет. Вещи, которые хорошо продуманы и проверены временем включаются в стандартную библиотеку(см. пункт "Перешло из boost"). Добавлено Цитата Gunnar @ К примеру работаешь ты с регулярными выражениями.1. Raw String Literals Вот вполне себе простенькая регулярка: ![]() ![]() \s+(\w+)\s+=\"((?:[^\"\\]|\\\"|\\n|\\t|...))*\" в с++03 её придётся записать так: ![]() ![]() char *rx = "\\s+(\\w+)\\s+=\\\"((?:[^\\\"\\\\]|\\\\\\\"|\\\\n|\\\\t|...))*\\\""; в с++09 можно будет записать так: ![]() ![]() char *rx = R"[\s+(\w+)\s+=\"((?:[^\"\\]|\\\"|\\n|\\t|...))*\"]"; Добавлено Цитата Gunnar @ 2. Prohibited access specifier к примеру, все классы, расчитанные на динамическое использование запрещают копирование своих экземпляров ![]() ![]() class I { private: // запрещаем I(const I &); I& operator =(const I &); public: // ... void foo(const I &); }; void I::foo(const I &a) { I b = a; // но запрет работает только для ф-ций не членов и не друзей. // здесь линкер ругнётся // и, не дай Б-г, кто-нибудь(ваш коллега, к примеру) додумается реализовать конструктор копирования - тогда не спасёт даже линкер } В С++09 добавили такой спецификатор доступа, который делает запрет даже ф-циям членам. ![]() ![]() class I { prohibited: // запрещаем I(const I &); I& operator =(const I &); public: // ... void foo(const I &); }; void I::foo(const I &a) { I b = a; // запрет работает даже для ф-ций членов - compile-time error } |
Сообщ.
#11
,
|
|
|
archimed7592, все понял... И как я сам не допер?
![]() Да новые возможности впечатляют. Не все еще вкурил окончательно, но это вопрос времени. Но вот сэтим они конечно отожгли. Паскалюгой попахивает (или бейсиком??) ![]() Цитата archimed7592 @ Range-based for-loop int array[5] = { 1,2,3,4,5 }; std::vector< int > vec = { 1, 2, 3, 4, 5 }; // так инициализировать нельзя, но мы это опустим ![]() for ( auto& x : array ) x *= 2; for ( float x : vec ) std::cout << x << std::endl; Ну и сборщик мусора на мой взгляд - лишний. Был один язык лишенный этого греха, и тот сдался. |
![]() |
Сообщ.
#12
,
|
|
Цитата Gunnar @ foreach - очень полезная фича. На данный момент либо используется std::for_each в связке с boost::lambda, либо кривоватый BOOST_FOREACH. Если это так часто используется, то почему бы не включить в стандарт поддержку на уровне языка?Паскалюгой попахивает (или бейсиком??) ![]() Цитата Gunnar @ Ну и сборщик мусора на мой взгляд - лишний. Был один язык лишенный этого греха, и тот сдался. Нет. Сборщик мусора будет исключительно опциональной фичей. Т.е. по умолчанию программа не будет знать ни о каком сборщике, а для того, чтобы пользоваться им придётся делать некоторые телодвижения. Скажу лишь, что в Symantec не дураки сидят(а именно они и занимаются добавлением GC в стандарт) и судя по их презенташкам(да и по логике вообще) сборка мусора должна только увеличить производительность в некоторого рода приложениях. |
Сообщ.
#13
,
|
|
|
archimed7592 а где презентации поглядеть можно?
|
![]() |
Сообщ.
#14
,
|
|
Xenon_Sk,
Programmer Directed GC for C++ Transparent Garbage Collection for C++ Сейчас наткнулся на ещё кое-какие документы про GC, может попозже обзорчик GC напишу. |
Сообщ.
#15
,
|
|
|
Ага, молитесь ещё, чтоб всё это поддерживалось новыми компиляторами
![]() ![]() Цитата ![]() ![]() string s3 = static_cast<string &&>(s1); // move from s1 to s2 Я на данный момент реализую это примерно так: ![]() ![]() struct A { typedef const std::nothrow_t &quick_move; // ... A(const A &); // copy-constructor A(A &, quick_move); // move-constructor A &lvalue() { return (*this); } }; A GetA(); void f() { A a1(/* ... */); A a2 = a1; // construct a2 as a1 copy A a3(a1, std::nothrow); // move from a1 to a3 A a4(GetA().lvalue(), std::nothrow); // move from temporary to a4 } Раньше я создавал move-конструкторы с передачей указателя, но Yak уговорил меня этого не делать :-) Цитата archimed7592 @ К примеру, теперь передача non-POD в элипсис приведёт к ошибке компиляции, а не к UB как в C++03. Я вот только одной вещи пока не могу понять. Сейчас можно обернуть такой вызов в sizeof, и тогда никакого undefined behavior нет. Но будет ли такое работать в компиляторах с поддержкой C++09? Цитата archimed7592 @ Перегрузка операторов Рассматривается возможность перегрузки операторов . .* static_cast<> const_cast<> dynamic_cast<> Лучше б они для результата встроенного оператора –>* тип ввели. |