Итак, в кратце, крупные нововведения следующие:
* rvalue references
* template aliases
* variadic templates
* concepts
* unicode characters/strings
* initializer lists
Примечание: имеется так же обзор того, чего НЕ будет в С++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) взяли следующие критерии:
* Для всех наборов a1,a2,...,aN, для которых запись g(a1,a2,...,aN) корректна(well-formed), запись f(a1,a2,...,aN) должна быть так же корректна.
* Для всех наборов a1,a2,...,aN, для которых запись g(a1,a2,...,aN) некорректна(ill-formed), запись f(a1,a2,...,aN) должна быть так же некорректна.
* Количество работы, которую придётся проделать для реализации такой идеально-перенаправляющей ф-ции f должно не более чем линейно зависеть от N.
Вот простейший пример:
template < class T1, class T2, class T3>
void f(T1 &a1, T2 &a2, T3 &a3)
{
g(a1, a2, a3);
}
Всё бы хорошо, но нельзя сделать вызов f(1, 2, 3).
template < class T1, class T2, class T3>
void f(const T1 &a1, const T2 &a2, const T3 &a3)
{
g(a1, a2, a3);
}
Можно сделать вызов f(1, 2, 3), но, если g хотя бы для одного из параметров берёт неконстантную ссылку, то - облом.
template<class A1> void f(A1 & a1)
{
g(a1);
}
template<class A1> void f(A1 const & a1)
{
g(a1);
}
Для перегруженного варианта всё отлично, кроме 3-го пункта, а именно, при росте числа параметров N, кол-во ф-ций, которые придётся написать, равное 2N, будет расти совсем нелинейно.
Короче говоря, текущий стандарт решить эту проблему не позволяет.
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 >:ype vec; // не очень красивая запись
Вторая же проблема выражается в том, что при использовании вышеобозначенного workaround'а перестаёт работать вывод шаблонных параметров.
template < class T >
void f(std::vector< T > &)
{ }
template < class T >
void f2(typename MyVector< T >:ype &)
{ }
std::vector< int > v;
MyVector< int >:ype 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:uple и иже с ним). Языковых средств для вытягивания значения из набора(args) вроде как нету, но, опять же, руками это делается несложно - std:
uple тому пример.
В документах встречалось упоминание, что значение можно вытянуть как из массива(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:(); // default constructor
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:ype() = 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:uple
Тож самое, что и boost:uple, за одним отличием - переведён на синтаксис 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. Также, возможность выполнения атомарных операций(необходимо для синхронизации - реализации спин-локов)