C++14 — неофициальное название версии стандарта C++ ISO/IEC JTC1 (полное название: «International Standard ISO/IEC 14882:2014(E) Programming Language C++»)[1]. C++14 можно рассматривать как небольшое расширение над C++11, содержащее в основном исправления ошибок и небольшие улучшения. Комитет разработки нового стандарта опубликовал черновик N3690 15 мая 2013[2]. Рабочая версия черновика N3936 была опубликована 2 марта 2014 года, заключительный период голосования закрыт 15 августа 2014 года, а результат (единогласное одобрение) был объявлен 18 августа 2014 года[3].
Поскольку разработка стандарта была длительной, а год выпуска итоговой версии не был определён, в ходе разработки также использовалось имя «C++1y», аналогично тому, как стандарт C++11 до его выпуска называли «C++0x» (выпуск этой версии ожидался до 2010 года).
Описанные ниже возможности языка соответствуют рабочему черновику N3797. В них могут быть небольшие различия по сравнению с окончательной версией стандарта.
Изменения в языкеПравить
В этом разделе приведены новые возможности ядра языка в C++14.
Вывод типа возвращаемого значения для функцийПравить
C++11 позволяет выводить тип возвращаемых значений для лямбда-функций из типа возвращаемого выражения. C++14 расширяет эту возможность на все функции. Новый стандарт также описывает вывод типов для лямбда-функций, с видом, отличным от return expression;
[4].
Для того, чтобы использовать автоматический вывод типа возвращаемого значения, функция должна быть объявлена с типом auto
в качестве типа возвращаемого значения, но без хвостового спецификатора типа возвращаемого значения из C++11:
auto DeduceReturnType(); // тип возвращаемого значения будет определён позже.
Если в теле функции в разных местах возвращаются несколько выражений, все эти выражения должны иметь общий выводимый тип[5].
Функции, использующие автовыведение типа возвращаемого значения, могут использовать предварительное объявление, но использовать их можно только после определения. Эти определения должны быть доступны в той же единице трансляции, в которой они используются.
В таких функциях возможно использовать рекурсию, но рекурсивный вызов должен выполняться хотя бы после одного возврата значения в этой функции[5]:
auto Correct(int i) {
if (i == 1)
return i; // в качестве типа возвращаемого значения выводится int
else
return Correct(i-1)+i; // теперь можно вызывать
}
auto Wrong(int i) {
if(i != 1)
return Wrong(i-1)+i; // неподходящее место для рекурсии. Нет предшествующего возврата.
else
return i; // в качестве типа возвращаемого значения выводится int
}
Альтернативный вывод типа при объявленииПравить
В C++11 были добавлены два способа вывода типов. auto
позволял создавать переменные с типом на основе присваиваемого выражения. decltype
позволял определить результирующий тип произвольного выражения. Однако типы, выводимые decltype
и auto
, различались между собой. В частности, auto
всегда выводит нессылочный тип, как если бы был обработан std::remove_reference
, тогда как auto&&
всегда выводит ссылочный тип. Тем не менее, результатом decltype
может быть как ссылочный тип, так и нессылочный, в зависимости от обрабатываемого выражения[4]:
int i;
int&& f();
auto x3a = i; // decltype(x3a) - int
decltype(i) x3d = i; // decltype(x3d) - int
auto x4a = (i); // decltype(x4a) - int
decltype((i)) x4d = (i); // decltype(x4d) - int&
auto x5a = f(); // decltype(x5a) - int
decltype(f()) x5d = f(); // decltype(x5d) - int&&
В C++14 добавлен синтаксис decltype(auto)
. Этот синтаксис позволяет использовать правила decltype
для объявлений auto
. Имеет смысл только в шаблонном коде.
Синтаксис decltype(auto)
также можно использовать для вывода типов возвращаемых значений, если указать decltype(auto)
вместо auto
на месте типа возвращаемого значения функции[5].
Уменьшение ограничений на константные выраженияПравить
В C++11 представлена концепция constexpr
-функций: функций, которые могут быть выполнены во время компиляции. Возвращаемые ими значения могут использоваться в операциях, где требуется константное выражение, например, в качестве аргумента шаблона. Тем не менее, в C++11 constexpr
-функции могут содержать только одно возвращаемое выражение (а также static_assert
и несколько других объявлений).
В C++14 эти ограничения частично сняты. constexpr
-функции теперь могут содержать следующие элементы[4]:
- Любые объявления, кроме:
static
илиthread_local
переменных;- объявлений переменных без инициализаторов.
- Условные инструкции ветвления
if
иswitch
. - Все инструкции циклов, в том числе
for
для диапазонов. - Выражения, изменяющие значения объектов, если время жизни этих объектов началось в
constexpr
-функции. К этому относятся также вызовы любых не-const
constexpr
нестатических функций-членов.
Инструкция goto
не допускается в constexpr
-функции C++14.
Ограничения на вызов не-constexpr
-функций остаются в силе. Таким образом, если используется for
для диапазонов, функции begin
и end
контейнеров должны быть перегружены как constexpr. Для встроенного типа std::initializer_list
функции begin/end
определены как constexpr, как локально, так и глобально.
Кроме того, в C++11 все нестатические методы, объявленные с constexpr
, неявно считались const
-функциями по отношению к this
. Это ограничение снято; нестатические методы теперь могут быть не const
[6]. Тем не менее, как упомянуто ранее, не-const
constexpr
-метод может изменить поля класса только в том случае, если время жизни этого объекта началось в ходе вычисления константного выражения.
Шаблоны переменныхПравить
В предыдущих версиях C++ шаблонизация применялась только для функций и классов. C++14 позволяет создавать шаблонные переменные.
template<typename T>
constexpr T pi = T(3.1415926535897932385);
// Usual specialization rules apply:
template<>
constexpr const char* pi<const char*> = "pi";
В этом примере определён шаблон переменной pi
, к которому можно обратиться, чтобы получить значение числа пи для различных типов (например, 3
, при чтении целого типа; наиболее близкое значение к float
, double
или long double
при чтении как float
, double
или long double
, соответственно, и т. д.).
К таким объявлениям и определениям относятся обычные правила шаблонов, в том числе правила специализации[7][8].
Агрегатная инициализация классов с инициализаторами полейПравить
В C++11 были добавлены инициализаторы полей классов — выражения, применяемые к полям на уровне класса, если конструктор их не инициализирует самостоятельно. Определение агрегатов было изменено, чтобы явно исключить все классы с инициализаторами членов, поэтому для них агрегатная инициализация была невозможна.
C++14 снимает это ограничение[4] и разрешает агрегатную инициализацию классов с инициализаторами полей. Если список инициализаторов в фигурных скобках не предоставляет значение для этого аргумента, то работу на себя возьмёт инициализатор поля[9].
Литералы двоичных чиселПравить
Числовые литералы в C++14 можно указать в двоичной форме[4]. Синтаксис использует префиксы 0b
или 0B
. Схожий синтаксис также используется в Java, Python, Perl и D.
Разделители разрядовПравить
В C++14 можно использовать апостроф для произвольного разделения разрядов в числовых литералах[10]. В некоторых случаях это упрощает восприятие больших числовых констант в коде и улучшает читаемость кода.
auto integer_literal = 1'000'000;
auto floating_point_literal = 0.000'015'3;
auto binary_literal = 0b0100'1100'0110;
auto silly_example = 1'0'0'000'00;
Обобщённые лямбда-функцииПравить
В C++11 параметры лямбда-функций требовалось объявлять с указанием конкретных типов. C++14 снимает это ограничение и позволяет объявлять параметры лямбда-функций со спецификатором типа auto
[7].
auto lambda = [](auto x, auto y) {return x + y;};
Выведение типов параметров обобщённых лямбда-функций выполняется по правилам, схожим с выведением типов для auto
-переменных (но не является полностью идентичным). Приведённый выше код эквивалентен следующему[11]:
struct unnamed_lambda
{
template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;}
};
auto lambda = unnamed_lambda();
Захват выражений для лямбда-функцийПравить
Лямбда-функции C++11 позволяют захватывать переменные, объявленные во внешней области видимости, путём передачи по ссылке или по значению. Это означает, что нельзя захватить по значению переменные типов, допускающих только перемещение (но не допускающих копирования)[12]. C++14 позволяет захватывать переменные с инициализацией произвольным выражением. Благодаря этому возможно захватывать переменные с перемещением значения и объявлять переменные с именами, не объявленными в более высоких областях видимости[7].
Захват выражений осуществляется с помощью инициализаторов:
auto lambda = [value = 1] {return value;};
Лямбда-функция lambda
вернёт 1, так как для параметра value
сработал соответствующий инициализатор. Тип захватываемого параметра выводится из типа инициализатора, подобно объявлению переменной со спецификатором auto
.
Эту возможность можно использовать для захвата с перемещением при помощи стандартной функции std::move
:
auto ptr = make_unique<int>(10);
auto lambda = [value = std::move(ptr)] {return *value;};
Атрибут [[deprecated]]
Править
Атрибут deprecated
позволяет отмечать сущности как устаревшие. Обращение к этим сущностям по-прежнему возможно, но при компиляции выводится предупреждение. В качестве аргумента deprecated
может выступать строковый литерал, объясняющий причину устаревания и/или возможную замену.
[[deprecated]] int f();
[[deprecated("g() не является потоко-безопасным. Используйте h() вместо g()")]]
void g( int& x );
void h( int& x );
void test() {
int a = f(); // warning: 'f' is deprecated
g(a); // warning: 'g' is deprecated: g() не является потоко-безопасным. Используйте h() вместо g()
}
Новые функции стандартной библиотекиПравить
Разделяемые мьютексы и блокировкиПравить
В C++14 добавлены разделяемые (shared) мьютексы и новый тип блокировки для таких мьютексов[13][14].
Гетерогенный поиск в ассоциативных контейнерахПравить
Стандартная библиотека C++ определяет четыре ассоциативных класса-контейнера. Эти классы позволяют пользователю искать значения на основе значения этого типа. Контейнеры map позволяют пользователю указать ключ и значение, при этом поиск производится по ключу и возвращает значение. Тем не менее, поиск всегда выполнялся по конкретному типу ключа, будь то ключ, как в map или само значение, как в set.
C++14 позволяет индексировать ассоциативные контейнеры значением произвольного типа при условии, что имеется перегруженный оператор сравнения, который может сравнивать значение этого типа со значением типа ключа контейнера[15]. Это позволяет индексировать map-контейнеры с типом ключа std::string
выражениями типа const char*
с использованием перегруженного оператора сравнения operator<
.
Для сохранения обратной совместимости, гетерогенный поиск допускается только тогда, когда компаратор, передаваемый ассоциативному контейнеру, поддерживает такой поиск. Классы стандартной библиотеки std::less
(по умолчанию для set- и map-контейнеров) и std::greater
позволяют осуществлять гетерогенный поиск[16].
Стандартные пользовательские литералыПравить
В C++11 описан синтаксис определяемых пользователем литеральных суффиксов, но ни один из них не используется в стандартной библиотеке. C++14 добавляет следующие стандартные литералы[15]:
- «s» для создания различных
std::basic_string
типов. - «h», «min», «s», «ms», «us» и «ns» для создания соответствующих временных интервалов
std::chrono::duration
.
string str = "hello world"s;
chrono::duration dur = 60s;
Два литерала «s» не влияют друг на друга, поскольку строковой литерал работает только со строками, а секундный действует только на числа[17].
Адресация к кортежам по типуПравить
std::tuple
, введённый в C++11, позволяет агрегировать несколько типизированных значений, которые будут проиндексированы во время компиляции. C++14 расширяет функциональность кортежей для возможности обращения к элементам кортежа не только по индексу, но и по типу[15]. Если кортеж содержит более одного элемента запрашиваемого типа, поиск приведёт к ошибке времени компиляции[18]:
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // то же, что и раньше: j == 7
string s = get<string>(t); // ошибка времени компиляции из-за неоднозначности
Прочие изменения стандартной библиотекиПравить
std::make_unique
можно использовать так же, как и std::make_shared
для объектов std::unique_ptr
[7].
Для std::integral_constant
добавлен перегруженный operator()
, возвращающий константное значение[15].
По аналогии с глобальными функциями std::begin/std::end
добавлены функции std::cbegin/std::cend
, которые возвращают константные итераторы на начало и конец диапазона.
ПримечанияПравить
- ↑ ISO/IEC 14882:2014 — Information technology — Programming languages — C++ (неопр.). ISO (14 января 2014). Дата обращения: 26 января 2015. Архивировано 29 января 2017 года.
- ↑ Committee Draft, Standard for Programming Language C++ (неопр.) (PDF). ISO (15 мая 2013). Дата обращения: 24 июля 2014. Архивировано 21 января 2022 года.
- ↑ Sutter, Herb (August 18, 2014), We have C++14!, <https://isocpp.org/blog/2014/08/we-have-cpp14>. Проверено 18 августа 2014. Архивная копия от 19 августа 2014 на Wayback Machine
- ↑ 1 2 3 4 5 Wong, Michael The View from the C++ Standard meeting April 2013 Part 3 (неопр.). C/C++ Cafe (30 апреля 2013). Дата обращения: 14 июня 2013. Архивировано 13 октября 2013 года.
- ↑ 1 2 3 Merrill, Jason N3638 Return type deduction for normal functions (Revision 5) (неопр.) (17 апреля 2013). Дата обращения: 14 июня 2013. Архивировано 25 августа 2013 года.
- ↑ Smith, Richard N3652 Relaxing constraints on constexpr functions (неопр.) (18 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 25 августа 2013 года.
- ↑ 1 2 3 4 Саттер, Герб Trip Report: ISO C++ Spring 2013 Meeting (неопр.). isocpp.org (20 апреля 2013). Дата обращения: 14 июня 2013. Архивировано 20 августа 2017 года.
- ↑ Dos Reis, Gabriel N3651 Variable Templates (Revision 1) (неопр.) (PDF) (19 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 25 августа 2013 года.
- ↑ Vandevoorde, Daveed; Voutilainen, Ville N3653 Member initializers and aggregates (неопр.) (17 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 25 августа 2013 года.
- ↑ Crowl, Lawrence; Smith, Richard; Snyder, Jeff; Vandevoorde, Daveed N3781 Single-Quotation-Mark as a Digit Separator (неопр.) (25 сентября 2013). Дата обращения: 15 октября 2014. Архивировано 13 апреля 2014 года.
- ↑ Faisal, Vali; Sutter, Herb; Abrahams, Dave N3649 Generic (Polymorphic) Lambda Expressions (Revision 3) (неопр.) (19 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 25 августа 2013 года.
- ↑ Move capture in Lambda (неопр.). Stack Overflow. Дата обращения: 24 июля 2014. Архивировано 24 января 2013 года.
- ↑ Wong, Michael The View from the C++ Standard meeting April 2013 Part 3 (неопр.). C/C++ Cafe (30 апреля 2013). Дата обращения: 14 июня 2013. Архивировано 13 октября 2013 года.
- ↑ Howard, Hinnant; Vollmann, Detlef; Boehm, Hans N3659 Shared locking in C++ (Revision 2) (неопр.) (19 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 19 августа 2013 года.
- ↑ 1 2 3 4 Wong, Michael The View from the C++ Standard meeting April 2013 Part 2 (неопр.). C/C++ Cafe (26 апреля 2013). Дата обращения: 14 июня 2013. Архивировано 13 октября 2013 года.
- ↑ N3657 Adding heterogeneous comparison lookup to associative containers (rev 4) (неопр.) (19 марта 2013). Дата обращения: 24 июля 2014. Архивировано 19 августа 2013 года.
- ↑ Peter, Sommerlad N3642 User-defined Literals for Standard Library Types (part 1 - version 4) (неопр.) (PDF) (18 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 25 августа 2013 года.
- ↑ Spertus, Mike N3670 Wording for Addressing Tuples by Type: Revision 2 (неопр.) (19 апреля 2013). Дата обращения: 24 июля 2014. Архивировано 19 августа 2013 года.