С++17 (также известный как C++1z) — это название версии стандарта C++ ISO/IEC. Спецификации для C++17 были опубликованы в декабре 2017 года[1][2].
std::map
и std::unordered_map
[3][4];std::size()
;boost::filesystem
[6];std::get
. Например: auto x = std::make_tuple(4,6,7); auto [a,b,c] = x;
if(auto a = getA(); a.isValid()) {}
Триграфы использовались для машин с нестандартной кодировкой и/или ограниченной клавиатурой. Ещё в конце 80-х, с распространением 8-битных кодировок и дешёвых резиномембранных клавиатур, триграфы фактически потеряли смысл, и тридцать лет спустя были закономерно исключены[10][11].
// Will the next line be executed????????????????/
a++; /* с триграфами эта строка закомментирована — триграф ??/ эквивалентен \ */
Язык Си был «переносимым ассемблером»: он позволял делать быстрые программы, компилирующиеся на разных компьютерах, к тому же использовал ассемблерные утилиты (компоновщик, библиотекарь). Понятия вроде «заголовочный файл» и «единица трансляции» — отголоски тех времён.
Слово register
изначально связанно с ручной оптимизацией программы. Слово всё ещё остаётся зарезервированным, но не означает ничего[12].
Операция явно небезопасна и запрещена ещё в Си++98[13]. Операция --
отсутствует и так.
Заявленные исключения void f() throw(A, B, C);
, имеющиеся, например, в Java, приносят больше вреда, чем пользы. Запрещены в Си++11, удалены в Си++17. Остался throw()
как синоним для noexcept(true)
[14].
Код struct X {static constexpr int n = 10;}; int X::n;
явно избыточен, но почему-то был разрешён. Функциональность constexpr
аналогична новым inline
-переменным[15].
В их числе std::auto_ptr
, std::random_shuffle
и старые функциональные адаптеры[16][17].
Вместо них используются unique_ptr
, shuffle
и новые функциональные шаблоны, основанные на function
/bind
. Заявляется, что любой код на auto_ptr
может быть механически преобразован в unique_ptr
, с простым добавлением std::move
там, где идёт передача владения.
Также удалены отдельные части iostream
, запрещённые ещё в Си++98[18].
Всего пять перегрузок, включая эту
template< class Alloc >
function( std::allocator_arg_t, const Alloc& alloc ) noexcept;
Из-за непонятной семантики и сложностей реализации их удалили без предварительного запрета[19].
Запрещены несколько редких возможностей стандартной библиотеки:[20][21][22]
allocator<void>
— оказался невостребованным;allocator
— дублируются allocator_traits
;raw_storage_iterator
— не вызывает конструкторов и потому ограничен по применению;get_temporary_buffer
— имеет неочевидные подводные камни;is_literal_type
— бесполезен для обобщённого кода, но оставлен, пока в Си++ существует понятие «литеральный тип»;iterator
— проще писать итераторы с нуля, чем основываться на нём;codecvt
— на поверку работал очень плохо, комитет призвал пользоваться специализированными библиотеками;shared_ptr::unique()
— из-за ненадёжности в многопоточной среде.Полностью удалить обещают в Си++20.
Из-за неадекватной семантики метод упорядочивания «consume» временно запретили, призвав пользоваться методом «acquire». Работа над новой семантикой всё ещё ведётся и, возможно, запрет когда-нибудь снимут[26].
С переходом на Си11 удалены заголовочные файлы <ccomplex>
, <cstdalign>
, <cstdbool>
, <ctgmath>
. Файл <ciso646>
не запрещён[27].
Функции void f() noexcept(true);
и void f() noexcept(false);
— теперь функции с разными типами (но не могут формировать перегруженный набор). Это позволит API требовать callback’и, которые не выбрасывают аварий[28].
В Си++11 появилась возможность создавать структуры данных, чьё выравнивание больше, чем теоретическое. Эта возможность была подхвачена операцией new[29].
class alignas(16) float4 {
float f[4];
};
float4 *p = new float4[1000];
Появилась перегрузка операции new с дополнительным параметром, чтобы корректно разместить в памяти чрезмерно выравненный объект.
Изменён смысл понятия prvalue: теперь это всего лишь инициализация.
В коде SomeType a = 10;
хоть всё ещё требуется и конструктор, и операция =, гарантированно будет вызван только конструктор.
Это значит, что функции могут возвращать типы, которые нельзя копировать и перемещать.
Теперь операции a.b
, a->b
, a->*b
, a(b1, b2, b3)
, b += a
(и аналоги для других операций), a[b]
, a << b
и a >> b
вычисляются в порядке a → b, чтобы держать под контролем побочные эффекты[30].
Если их вызвать как функции (например, operator += (a, b)
), порядок остаётся неопределённым.
Существуют шаблоны, принимающие константу.
template <int N> struct Array
{
int a[N];
};
Что может быть константой N, и что не может — объявлено от противного. Константа в шаблоне не может быть указателем на поле, временный объект, строковый литерал, результат typeid
и стандартную переменную __func__
[25][31];
Теперь for (auto v : x)
означает auto __begin = begin-expr; auto __end = end-expr;
, позволяя begin и end разных типов.
Это — база для прохода по диапазонам (ranges), работа над которыми продолжается[32].
Массивы std::vector и std::string имеют дело с непрерывными участками памяти. Для них ввели понятие «непрерывный итератор»[4][33]. Концептуально ничего не изменилось.
Ранее подобное поведение определялось реализацией.
Заодно сделали «символы UTF-8», которые имеют тип char
и могут держать коды от 0 до 255, по аналогии со строками UTF-8 — по видимому, чтобы программа меньше зависела от настроек локали на компьютере[25][34].
Если static_assert
не сработал, не всегда требуется сообщать программисту, что не так — часто он и сам может понять из констекста.[35].
static_assert(sizeof(wchar_t) == 2);
[[fallthrough]]
: в одном из разделов оператора switch
мы намеренно «проваливаемся» в следующий.[[nodiscard]]
: вызов функции как процедуры считается ошибкой — например, это «чистая» функция вроде string::empty()
[36], вся работа которой заключается в возврате значения, или протокол работы с объектом требует что-то сделать с возвращённым значением, как в unique_ptr::release()
.[[maybe_unused]]
: в каком-то из режимов компиляции (Windows/POSIX, отладка/выпуск) тот или иной элемент не используется, и это не ошибка.Недоработка языка Си++: в шаблонах typename
и class
кое-где не взаимозаменяемые[37].
template<template<typename> class X> struct C; // OK
template<template<typename> typename X> struct D; // не компилируется
Оба ключевых слова явно объявлены взаимозаменяемыми.
Добавленный в Си++11 универсальный инициализатор int x{};
позволяет одним синтаксисом создать объект, структуру, массив. В Си++17 уточнено: если вместо типа стоит auto
— пользователь хочет создать один объект и никаких initializer_list не нужно.
При этом auto x = {1, 2, 3};
продолжает создавать: с одной стороны, для совместимости с for (auto x : {1, 2, 3})
, с другой — для одного объекта есть auto x = 1;
[38][17].
auto x1 = { 3 }; // std::initializer_list<int>
auto x2 { 1, 2 }; // теперь ошибка
auto x3 { 3 }; // int
Определение вложенных пространств имён:[17][39] namespace A::B {}
как сокращение для namespace A { namespace B {} }
;
Например:
enum class TriBool {
NO,
MAYBE,
YES,
NN [[maybe_unused]]
};
constexpr int TriBool_N = static_cast<int>(TriBool::NN);
const char* triBoolNames[TriBool_N] = { "no", "maybe", "yes" };
Какой-то заявленной цели пока нет[25][40], но это позволит разработчикам компиляторов придумать таковую — например, объявить, что элемент NN особый и его не надо присваивать переменным, обрабатывать в switch
.
Концепция SFINAE позволила сделать несложный шаблон enable_if
, который обеспечивает разную функциональность для разных типов, но даёт тяжеловесный код. В Си++17 можно упростить программу: оператор if constexpr(expression)
инстанцирует код, если выражение в скобках истинно[41].
template <class T>
constexpr T absolute(T arg) {
return arg < 0 ? -arg : arg;
}
template <class T>
constexpr auto precision_threshold = T(0.000001);
template <class T>
constexpr bool close_enough(T a, T b) {
if constexpr (is_floating_point_v<T>) // << !!
return absolute(a - b) < precision_threshold<T>;
else
return a == b;
}
В данном случае мы убеждаемся, что разница между дробными числами невелика, а целые просто проверяем на равенство.
Упакованные выражения[25][42]:
template<typename... As> void foo(As... args)
{ return (args && ...); }
Языки Си и Си++ разрабатываются разными комитетами. Стандартную библиотеку обновили с C99 до C11[43].
Часто бывает нужно передать неизменную строку в другой участок кода, это можно сделать такими методами:
void doSmth(const char *s);
void doSmth(const std::string &s);
Если методика владения памятью другая, приходится проводить преобразование.
В C++17 появился тип string_view — строка, имеющая только указатель и длину, без владения и управления памятью.
Есть две новые константы, hardware_constructive_interference_size
и hardware_destructive_interference_size
. Таким образом пользователь может избежать ложного общего доступа (destructive interference) и улучшить локальность (constructive interference).
struct keep_apart {
alignas(hardware_destructive_interference_size) atomic<int> cat;
alignas(hardware_destructive_interference_size) atomic<int> dog;
// cat далеко от dog, их можно менять из разных потоков.
};
struct together {
atomic<int> dog;
int puppy;
};
struct kennel {
//...
alignas(sizeof(together)) together pack;
//...
};
static_assert(sizeof(together) <= hardware_constructive_interference_size);
// убеждаемся, что together занимает одну строку кэша.
Теоретически обе константы должны быть одинаковыми, но для поддержки неоднородных архитектур решено было сделать две константы.[44]
В библиотеке появились функции, так называемые deduction guides, позволяющие делать такое:
std::pair p(2, 4.5); // 1
std::vector<int> v = {1, 2, 3, 4};
std::vector x(v.begin(), v.end()); // 2
clear()
писал empty()
.Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".
Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.
Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .