C++0x

Материал из Seo Wiki - Поисковая Оптимизация и Программирование

Перейти к: навигация, поиск

C++0x — будущая версия стандарта языка C++, вместо ныне существующего ISO/IEC 14882:2003. Новый стандарт будет включать дополнения в ядре языка и расширение STL, включая большую часть TR1 — кроме, вероятно, библиотеки специальных математических функций. Учитывая, что работа над стандартом ещё не завершена — данная статья, возможно, не будет точно соответствовать конечному варианту стандарта. Самая последняя версия будущего стандарта опубликована на сайте комитета ISO C++.

ISO/IEC JTC1/SC22/WG21 Комитет Стандартизации C++ намеревается опубликовать новый стандарт в 2009 (соответственно стандарт который сейчас называют C++0x будет называться C++09). Чтобы успеть, Комитет решил сосредоточиться на предложениях поступивших до 2006 и игнорировать более новые[1].

Языки программирования, такие как C++, проходят эволюционное развитие своих возможностей. Этот процесс неизбежно вызывает проблемы совместимости с уже существующим кодом. Согласно докладу, сделанному Бьёрном Страуструпом (изобретатель языка С++ и член Комитета), новый стандарт будет на 100% совместим с нынешней версией языка С++[2].

Содержание

Предполагаемые изменения стандарта

Как уже было сказано, изменения коснутся как ядра С++ так и его стандартной библиотеки.

При разработке каждого раздела будущего стандарта, комитет использовал ряд правил:

  • Поддержка стабильности языка и обеспечение совместимости с C++98 и, по возможности, с Си;
  • Предпочитается введение новых возможностей через стандартную библиотеку, а не через ядро языка;
  • Предпочитаются изменения которые улучшают технику программирования;
  • Совершенствовать C++ с точки зрения системного и библиотечного дизайна, вместо введения новых возможностей полезных для отдельных приложений;
  • Увеличивать типобезопасность для обеспечения безопасной альтернативы для нынешних опасных подходов;
  • Увеличивать производительность и возможности работать напрямую с аппаратной частью;
  • Обеспечивать решение реальных распространённых проблем;
  • Реализовать принцип «не платить за то, что не используешь»;
  • Сделать C++ проще для изучения без удаления возможностей, используемых программистами-экспертами.

Уделяется внимание новичкам, которые всегда будут составлять большую часть программистов. Многие новички не стремятся углублять уровень владения С++ ограничиваясь его использованием при работе над узкими специфичными задачами[1]. Кроме того, учитывая универсальность С++ и обширность его использования (включая как разнообразие приложений, так и стилей программирования), даже профессионалы могут оказаться новичками при использовании новых парадигм программирования.

Расширение ядра С++

Первоочередная задача комитета — развитие ядра языка С++. Дата представления C++0x зависит от успехов в этой части стандарта.

Ядро будет значительно усовершенствовано, будет добавлена поддержка многопоточности, поддержка обобщённого программирования, унификация инициализации и будут проведены работы по повышению его производительности.

Для удобства, возможности ядра и его изменения разделены на три основные части: повышение производительности, повышение удобства и новая функциональность. Отдельные элементы могут относиться к нескольким группам, но описываться будут только в одной - наиболее подходящей.

Повышение производительности за счёт ядра языка

Эти компоненты языка введены для уменьшения затрат памяти или увеличения производительности.

Ссылки на временные объекты/Семантика переноса (Rvalue Reference/Move semantics)

В стандарте C++, временные объекты (оригинальный термин «R-values», так как они порождаются с правой стороны выражения) можно передавать в функции, но только как константную ссылку (const &). Следовательно, функция не в состоянии определить временный это объект или нормальный, который тоже передали как const &. Кроме того, объект переданный как const & больше нельзя модифицировать (легально).

В C++0x будет добавлен новый тип ссылки — ссылка на временный объект (R-value reference). Его объявление следующее: typename &&. Оно может быть использовано как неконстантный, легально модифицируемый объект. Данное нововведение позволяет учитывать временные объекты и реализовывать семантику переноса (Move semantics).

Например, std::vector это простая обёртка вокруг Си-массива и переменной хранящей его размер. Если std::vector создаётся как временный объект или возвращается из функции — можно создавая новый объект просто перенести все внутренние данные из ссылки нового типа. Конструктор переноса std::vector через полученную ссылку на временный объект просто копирует указатель массива находящийся в ссылке, которая по окончании устанавливается в пустое состояние.

Обобщённые константные выражения

В C++ всегда присутствовала концепция константных выражений. Так, выражения типа 3+4 всегда возвращали одни и те же результаты, не вызывая никаких побочных эффектов. Сами по себе константные выражения предоставляют компиляторам C++ удобные возможности по оптимизации результата компиляции. Компиляторы вычисляют результаты таких выражений только на этапе компиляции и сохраняют уже вычисленные результаты в программе. Таким образом, подобные выражения вычисляются только раз. Также существует несколько случаев, в которых стандарт языка требует использования константных выражений. Такими случаями, например, могут быть определения массивов или значения перечислений (enum).


int GetFive() {return 5;}
 
int some_value[GetFive() + 7]; // создание массива 12 целых; запрещено в C++

Вышеуказанный код запрещён в C++, поскольку GetFive() + 7 фактически не является константным выражением, известным на этапе компиляции. Компилятору на тот момент просто не известно, что функция на самом деле возвращает константу во время исполнения. Причиной таких рассуждений компилятора является то, что эта функция может повлиять на состояние глобальной переменной, вызвать другую константную функцию не времени исполнения и т. д.

C++0x вводит ключевое слово constexpr, которое позволяет пользователю гарантировать, что или функция или конструктор объекта возвращает константу времени компиляции. Код выше может быть переписан следующим образом:

constexpr int GetFive() {return 5;}
 
int some_value[GetFive() + 7]; // создание массива 12 целых; разрешено в C++0x

Такое ключевое слово позволяет компилятору понять и удостовериться в том, что GetFive возвращает константу.

Использование constexpr порождает очень жёсткие ограничения на действия функции:

  1. Такая функция не может быть типа void.
  2. Тело функции должно быть вида return выражение.
  3. Выражение должно также быть константой, а также может вызывать только те функции, что также обозначены ключевым словом constexpr, или просто использовать обычные константы.
  4. Функция, обозначенная constexpr, не может вызываться до того момента, пока она не определена в текущей единице компиляции.

Переменные также могут быть определены как значения константных выражений:

constexpr double accelerationOfGravity = 9.8;
constexpr double moonGravity = accelerationOfGravity / 6;

Такие переменные уже неявно считаются обозначенными ключевым словом const. В них могут содержаться только результаты константных выражений или конструкторы таких выражений.

В случае необходимости конструирования константных значений из типов, определённых пользователем, конструкторы таких типов также могут быть описаны с помощью constexpr. Конструктор константных выражений подобно константным функциям также должен быть определён до момента первого его использования в текущей единице компиляции. У такого конструктора должно быть пустое тело, а также такой конструктор должен инициализировать члены своего типа только константами.


Изменения в определении простых данных (plain old data или POD)

В стандартном C++ только структуры, удовлетворяющие определённому набору правил, могут рассматриваться как тип простых данных (plain old data type или POD). Существуют веские причины ожидать расширения этих правил, с тем чтобы большее число типов рассматривались как POD. Типы, удовлетворяющие этим правилам, могут использоваться в реализации объектного слоя, совместимого с C. Однако в C++03 список этих правил чрезмерно строгий.

C++0x ослабит несколько правил, касающихся определения типов простых данных.

Класс/структура рассматриваются как типы простых данных если они тривиальные, стандартные (standard-layout???) и если все их не статические члены также являются типами простых данных.

Тривиальный класс или структура удовлетворяют нижеследующим правилам:

  1. Они могут иметь только тривиальный конструктор по умолчанию. Может быть использован синтаксис конструктора по умолчанию (SomeConstructor() = default;).
  2. Они могут иметь только тривиальный копирующий конструктор по умолчанию. Может быть использован тот же синтаксис.
  3. Они могут иметь только тривиальный оператор присваивания копии.
  4. Они могут иметь только тривиальный деструктор, который не должен быть виртуальным.

Стандартный класс или структура удовлетворяют следующим правилам:

  1. Они могут иметь только статические члены не стандартных типов.
  2. Они имеют один и тот же спецификатор доступа (public, private, protected) для всех не статических методов.
  3. Не имеют виртуальных функций.
  4. Не имеют виртуальных классов-родителей.
  5. Их родительские классы могут быть только стандартными.
  6. Не имеют родительского класса того же типа, что и первый объявленный не виртуальный член.
  7. Либо не имеют родительских классов содержащих не статические методы, либо не содержат членов не статических типов в большинстве наследующих классов и не более одного родительского класса с не статическими членами. Другими словами в иерархии классов только один класс может содержать не статические члены.

Ускорение компиляции языка

Внешние шаблоны

В стандарном С++ компилятор должен инстанцировать шаблон всякий раз, когда встречает в единице трансляции его полную специализацию. Это может существенно увеличить время компиляции, особенно в тех случаях, когда шаблон инстанцирован с одинаковыми параметрами в большом числе единиц трансляции. На данный момент не существует способа указать С++, что инстанцирования быть не должно.

В C++0x введена идея внешних шаблонов. В С++ уже есть синтаксис для указания компилятору того, что шаблон должен быть инстанцирован в определённой точке:

template class std::vector<MyClass>;

В С++ не хватает возможности запретить компилятору инстанцировать шаблон в единице трансляции. C++0x просто расширяет данный синтаксис:

extern template class std::vector<MyClass>;

Данное выражение говорит компилятору не инстанцировать шаблон в данной единице трансляции.

Улучшения в практическом использовании языка

Эти возможности предназначены для того, чтобы упростить использование языка. Они позволяют усилить типобезопасность, минимизировать дублирование кода, усложняют ошибочное использование кода и т. п.

Списки инициализации

Концепция списков инициализации пришла в C++ из C. Идея состоит в том, что структура или массив могут быть созданы передачей списка аргументов в порядке, соответствующем порядку определения членов структуры. Списки инициализации рекурсивны, что позволяет их использовать для массивов структур и структур, содержащих вложенные структуры. Списки инициализации очень полезны для статических списков и в тех случаях, когда требуется инициализировать структуру определённым значением. C++ также содержит конструкторы, которые могут содержать общую часть работы по инициализации объектов. Стандарт C++ позволяет использовать списки инициализации для структур и классов при условии, что те соответствуют определению простого типа данных (Plaid Old Data - POD). Классы, не являющиеся POD, не могут использовать для инициализации списки инициализации, в том числе это касается и стандартных контейнеров C++, таких как вектора.

C++0x связал концепцию списков инициализации и шаблонный класс, названный std::initializer_list. Это позволило конструкторам и другим функциям получать списки инициализации в качестве параметров. Например:

class SequenceClass
{
public:
  SequenceClass(std::initializer_list<int> list);
};

Данное описание позволяет создать SequenceClass из последовательности целых чисел следующим образом:

SequenceClass someVar = {1, 4, 5, 6};

Данный конструктор является особой разновидностью конструкторов, называемой конструкторы посредством списков инциализации. Классы, содержащие подобные конструкторы, обрабатываются особым образом во время инициализации.

Класс std::initializer_list<> является типом, определённым в стандартной библиотеке C++0x. Однако объекты данного класса могут быть созданы компилятором C++0x только статически с использованием синтаксиса со скобками {}. Список может быть скопирован после создания, однако это будет копированием по ссылке. Список инициализации является константным: ни его члены ни их данные не могут быть изменены после создания.

Так как std::initializer_list<> является полноценным типом, он может быть использован не только в конструкторах. Обычные функции могут получать типизированные списки инициализации в качестве аргумента, например:

void FunctionName(std::initializer_list<float> list);
 
FunctionName({1.0f, -3.45f, -0.4f});

Стандартные контейнеры могут быть инициализированы следующим образом:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" };

Универсальная инициализация

В стандарте C++ содержится ряд проблем, связанных с инициализацией типов. Существует несколько путей инициализации типов и не все они приводят к одинаковым результатам. К примеру, традиционный синтаксис инициализирующего конструктора может выглядеть как описание функции, и нужно предпринять дополнительные меры, чтобы компилятор не ошибся при анализе. Только агрегирующие типы и POD типы могут быть инициализированы с помощью инициализаторов агрегатов (вида SomeType var = {/*stuff*/};).

C++0x предоставляет синтаксис, позволяющий использовать единую форму инициализации для всех видов объектов с помощью расширения синтаксиса списков инициализации:

struct BasicStruct
{
 int x;
 double y;
};
 
struct AltStruct
{
  AltStruct(int x, double y) : x_{x}, y_{y} {}
 
private:
  int x_;
  double y_;
};
 
BasicStruct var1{5, 3.2};
AltStruct var2{2, 4.3};

Инициализация var1 работает точно также как и при инициализации агрегатов, т.е. каждый объект будет инициализирован копированием соответствующего значения из списка инициализации. При необходимости будет применено неявное преобразование типов. Если нужного преобразования не существует, программа будет считаться некорректно сформированной. Во время инициализации var2 будет вызван конструктор.

Предоставлена возможность писать подобный код:

struct IdString
{
  std::string name;
  int identifier;
};
 
IdString GetString()
{
  return {"SomeName", 4}; // Обратите внимание на отсутствие явного указания типов
}

Универсальная инициализация не заменяет полностью синтаксиса инициализации с помощью конструктора. Если в классе есть конструктор, принимающий в качестве аргумента список инициализации (ИмяТипа(initializer_list<SomeType>);), он будет иметь более высокий приоритет по сравнению с другими возможностями создания объектов. Например, в C++0x std::vector содержит конструктор, принимающий в качестве аргумента список инициализации:

std::vector<int> theVec{4};

Данный код приведёт к вызову конструктора, принимающего в качестве аргумента список инициализации, а не конструктор с одним параметром, создающий контейнер заданного размера. Для вызова этого конструктора пользователь должен будет использовать стандартный синтаксис вызова конструктора.

Вывод типов

В стандартном C++ (и C) тип переменной должен быть явно указан. Однако, после появления шаблонных типов и техник шаблонного метапрограммирования, тип некоторых вещей, в особенности возвращаемое значение функций, не может быть легко задан. Это приводит к сложностям при хранении промежуточных данных в переменных, иногда может потребоваться знание внутреннего устройства конкретной библиотеки метапрограммирования.

C++0x предлагает два способа для смягчения этих проблем. Во-первых, определение явно инициализируемой переменной может содержать ключевое слово auto. Это приведёт к тому, что будет создана переменная типа инициализирующего значения:

auto someStrangeCallableType = boost::bind(&SomeFunction, _2, _1, someObject);
auto otherVariable = 5;

Типом someStrangeCallableType будет то, что вернёт конкретная реализация шаблонной функции boost::bind для заданных аргументов. Данный тип будет легко определён компилятором во время выполнения семантического анализа, при том, что программисту для определения типа пришлось бы провести ряд изысканий.

Тип otherVariable также чётко определён, однако также легко может быть определён и программистом. Этот тип — int, такой же как у целочисленной константы.

Кроме того, для определения типа выражения во время компиляции может быть использовано ключевое слово decltype. Например:

int someInt;
decltype(someInt) otherIntegerVariable = 5;

Использование decltype наиболее полезно совместно с auto, так как тип переменной, описанной как auto<tt> известен только компилятору. Кроме того использование <tt>decltype может быть весьма полезным в выражениях, использующих перегрузку операторов и специализацию шаблонов.

auto также может быть использован для уменьшения избыточности кода. Например, вместо:

for (vector<int>::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr)

программист сможет написать:

for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)

Разница становится особенно заметной, когда программист использует большое число различный контейнеров, несмотря на то, что и сейчас существует хороший путь для уменьшения избыточного кода — использование typedef.

Тип, помеченный как decltype может отличаться от типа выведенного с помощью auto.

#include <vector>
int main()
{
  const std::vector<int> v(1);
  auto a = v[0];        // тип a - int  
  decltype(v[0]) b = 1; // тип b - const int& (возвращаемое значение
                        // std::vector<int>::operator[](size_type) const)
  auto c = 0;           // тип c - int   
  auto d = c;           // тип d - int            
  decltype(c) e;        // тип e - int, тип сущности, именнованной как c 
  decltype((c)) f = c;  // тип f - int&, так как (c) является lvalue
  decltype(0) g;        // тип g - int, так как 0 является rvalue
}

For-цикл по коллекции

В стандартном C++ для пробега элементов коллекции требуется масса кода. В некоторых языках, например в C#, есть средства, предоставляющие "foreach"-инструкцию, которая автоматически пробегает коллекцию от начала до конца. C++0x вводит подобное средство. Инструкция for позволит проще осуществлять пробег по коллекции элементов:

int my_array[5] = {1, 2, 3, 4, 5};
for(int &x : my_array)
{
  x *= 2;
}

Эта форма for, называемая в английском языке "range-based for", пробежит каждый элемент коллекции. Это будет применимо к C-массивам, спискам инициализаторов и любым другим типам, для которых определены функции begin() и end(), возвращающие итераторы. Все контейнеры стандартной библиотеки, имеющие пару begin/end, будут работать с for-инструкцией по коллекции.

Лямбда-функции и выражения

Альтернативный синтаксис функций

Улучшение конструкторов объектов

Явная перегрузка виртуальных функций

Константа для нулевого указателя

Строго типизированные перечисления

Угловые скобки

Локальные и безымянные типы в качестве агрументов шаблонов

Явные преобразования операторов

Символы и строки в Юникоде

"Сырые" строки (Raw string literals)

Статическая диагностика

Template typedefs

Шаблоны с переменным количеством аргументов

Сборщик мусора

Неограниченные объединения

Улучшения в функциональности ядра

Примечания

  1. 1,0 1,1 The C++ Source Bjarne Stroustrup (January 2, 2006) A Brief Look at C++0x.(англ.)
  2. C/C++ Users Journal Bjarne Stroustrup (May, 2005) The Design of C++0x: Reinforcing C++’s proven strengths, while moving into the future.(англ.)

Ссылки

es:C++0x fr:C++1x it:C++0x ja:C++0x ko:C++0x pl:C++0x pt:C++0x sv:C++#Historia th:C++0x zh:C++0x

Источник — «http://www.sbup.com/wiki/C%2B%2B0x»
Личные инструменты

Served in 0.315 secs.