C++17

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

С++17 (также известный как C++1z) — это название версии стандарта C++ ISO/IEC. Спецификации для C++17 были опубликованы в декабре 2017 года[1][2].

Значение константы __cplusplus стало 201703L, это используется для условной компиляции.

Возможности[править | править код]

  • Параллельные версии алгоритмов STL[3];
  • Новый синтаксис для распаковки пар, кортежей и прочих типов, для которых реализован std::get. Например: auto x = std::make_tuple(4,6,7); auto [a,b,c] = x;

Удалены или запрещены[править | править код]

Удалены триграфы[править | править код]

Триграфы использовались для машин с нестандартной кодировкой и/или ограниченной клавиатурой. Ещё в конце 80-х, с распространением 8-битных кодировок и дешёвых резиномембранных клавиатур, триграфы фактически потеряли смысл, и тридцать лет спустя были закономерно исключены[4][5].

// Will the next line be executed????????????????/
a++;      /* с триграфами эта строка закомментирована — триграф ??/ эквивалентен \ */

Удалено ключевое слово register[править | править код]

Язык Си был «переносимым ассемблером»: он позволял делать быстрые программы, компилирующиеся на разных компьютерах, к тому же использовал ассемблерные утилиты (компоновщик, библиотекарь). Понятия вроде «заголовочный файл» и «единица трансляции» — отголоски тех времён.

Слово register изначально связано с ручной оптимизацией программы. Современные компиляторы «под капотом» делают огромное количество оптимизаций, и подобное ручное управление представляется излишним. Ещё в Си++11 слово объявили нежелательным. Слово всё ещё остаётся зарезервированным, и его могут когда-нибудь задействовать с другой целью — как в Си++11 auto[6].

Удалена операция ++ для bool[править | править код]

Операция явно небезопасна и запрещена ещё в Си++98[7]. Операция -- отсутствует и так.

Удалены заявленные исключения[править | править код]

Заявленные исключения void f() throw(A, B, C);, имеющиеся, например, в Java, приносят больше вреда, чем пользы. Запрещены в Си++11, удалены в Си++17. Остался throw() как синоним для noexcept(true)[8].

Удалены типы и функции, получившие замену (и ставшие запрещёнными) в Си++11[править | править код]

В их числе std::auto_ptr, std::random_shuffle и старые функциональные адаптеры[9][10].

Вместо них используются unique_ptr, shuffle и новые функциональные шаблоны, основанные на function/bind. Заявляется, что любой код на auto_ptr может быть механически преобразован в unique_ptr, с простым добавлением std::move там, где идёт передача владения.

Также удалены отдельные части iostream, запрещённые ещё в Си++98[11].

Удалены конструкторы для std::function, принимавшие аллокатор[править | править код]

Всего пять перегрузок, включая эту

template< class Alloc >
function( std::allocator_arg_t, const Alloc& alloc ) noexcept;

Из-за непонятной семантики и сложностей реализации их удалили без предварительного запрета[12].

Запрещены крайне редкие возможности стандартной библиотеки[править | править код]

Запрещены несколько редких возможностей стандартной библиотеки:[13][14][15]

  • allocator<void> — оказался невостребованным;
  • часть функций allocator — дублируется шаблоном allocator_traits;
  • raw_storage_iterator — не вызывает конструкторов и потому ограничен по применению;
  • get_temporary_buffer — имеет неочевидные подводные камни;
  • is_literal_type — бесполезен для обобщённого кода, но оставлен, пока в Си++ существует понятие «литеральный тип»;
  • iterator — проще писать итераторы с нуля, чем основываться на нём;
  • codecvt — на поверку работал очень плохо, комитет призвал пользоваться специализированными библиотеками;
  • shared_ptr::unique() — из-за ненадёжности в многопоточной среде.

Полностью удалить обещают в Си++20.

Запреты, связанные с новыми функциями Си++17[править | править код]

  • result_ofinvoke_result — более простой синтаксис, основанный на выведении типов Си++11[16];
  • bool uncaught_exception()int uncaught_exceptions() — когда пользователь, например, выдернул флэшку и начали «сыпаться» аварии, может «висеть» необработанными и несколько исключений[17][18].

Удалены заголовки библиотеки Си[править | править код]

С переходом на Си11 удалены заголовочные файлы <ccomplex>, <cstdalign>, <cstdbool>, <ctgmath>. Файл <ciso646> не запрещён[19].

Глобальные изменения[править | править код]

Спецификация исключений — теперь часть системы типов[править | править код]

Функции void f() noexcept(true); и void f() noexcept(false); — теперь функции с разными типами (но не могут составлять перегруженный набор). Это позволит API требовать callback’и, которые не выбрасывают исключений[20].

New с чрезмерным выравниванием[править | править код]

В Си++11 появилась возможность создавать структуры данных, чьё выравнивание больше, чем теоретическое. Эта возможность была подхвачена операцией new[21].

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, чтобы держать под контролем побочные эффекты[22].

Если их вызвать как функции (например, operator += (a, b)), порядок остаётся неопределённым.

Расширили понятие «константа в шаблоне»[править | править код]

Существуют шаблоны, принимающие константу.

template <int N> struct Array
{
  int a[N];
};

Что может быть константой N, и что не может — объявлено от противного. Константа в шаблоне не может быть указателем на поле, на временный объект, на строковый литерал, на результат typeid и на стандартную переменную __func__[18][23];

В for могут быть begin и end разного типа[править | править код]

Теперь for (auto v : x) означает auto __begin = begin-expr; auto __end = end-expr;, допуская begin и end разных типов.

Это — база для прохода по диапазонам (ranges), работа над которыми продолжается[24].

Редакционные правки[править | править код]

Понятие «непрерывный итератор»[править | править код]

Массивы std::vector и std::string имеют дело с непрерывными участками памяти. Для них ввели понятие «непрерывный итератор»[25][26]. Концептуально ничего не изменилось.

Дали определения и другим понятиям — forwarding reference, default member initializer, templated entity. Это работа над концепциями Си++20.

Запрещены символы u'x' и U'x', не кодируемые одним символом[править | править код]

Ранее подобное поведение определялось реализацией.

Заодно сделали «символы UTF-8», которые имеют тип char и могут держать коды от 0 до 127, по аналогии со строками UTF-8 — по видимому, чтобы программа меньше зависела от настроек локали на компьютере[18][27].

Временно запрещён memory_order_consume[править | править код]

Из-за неадекватной семантики метод упорядочивания «consume» устно (то есть без отметки [[deprecated]]) запретили, призвав пользоваться методом «acquire». Работа над новой семантикой всё ещё ведётся и, возможно, запрет когда-нибудь снимут[28].

В любом случае на PowerPC и ARM все загрузки автоматически будут consume, но не все — acquire, и метод consume может сберечь такты в кроссплатформенном коде[29].

Язык[править | править код]

static_assert с одним аргументом[править | править код]

Если static_assert не сработал, не всегда требуется сообщать программисту, что не так — часто он и сам может понять из констекста.[30].

static_assert(sizeof(wchar_t) == 2);

Новые стандартные аннотации[править | править код]

  • [[fallthrough]]: в одном из разделов оператора switch мы намеренно «проваливаемся» в следующий. Возможная реализация устройства Даффа
int n = (count + 7) / 8;
if (!count) return;
switch (count % 8) {
case 0: do { *to = *from++; [[fallthrough]];
case 7:      *to = *from++; [[fallthrough]];
case 6:      *to = *from++; [[fallthrough]];
case 5:      *to = *from++; [[fallthrough]];
case 4:      *to = *from++; [[fallthrough]];
case 3:      *to = *from++; [[fallthrough]];
case 2:      *to = *from++; [[fallthrough]];
case 1:      *to = *from++;
        } while (--n > 0);
}
  • [[nodiscard]]: вызов функции как процедуры считается ошибкой — например, это «чистая» функция вроде string::empty()[31], вся работа которой заключается в возврате значения, или протокол работы с объектом требует что-то сделать с возвращённым значением, как в unique_ptr::release().
class SmartPtr {  // интерфейс
public:
   [[nodiscard]] Payload* release();
};

SmartPtr p;
p.release();   // warning: ignoring return value of 'SmartPtr::release()', declared with attribute nodiscard
  • [[maybe_unused]]: в каком-то из режимов компиляции (Windows/POSIX, отладка/выпуск) тот или иной элемент не используется, и это не ошибка.
void doSmth([[maybe_unused]] Logger& log)
{
#ifdef DEBUG
  log.put("doSmth IN");
#endif
}
Или параметр намеренно не используется, но имя оставлено для документирования.
class ISoccerSeason {  // интерфейс
public:
   /// @pre  обе команды участвуют в этом сезоне.
   /// В типичном футбольном сезоне обе команды сыграют и на своём, и на чужом поле.
   virtual bool doTeamsPlay([[maybe_unused]] const Team& home, [[maybe_unused]] const Team& away) const { return true; }
   virtual ~ISoccerSeason() = default;
};

Использование typename во вложенных шаблонах[править | править код]

Недоработка языка Си++: в шаблонах typename и class кое-где не взаимозаменяемые[32].

template<template<typename> class X> struct C;    // OK
template<template<typename> typename X> struct D; // не компилируется

Оба ключевых слова явно объявлены взаимозаменяемыми.

auto x{}; больше не создаёт initializer_list[править | править код]

Добавленный в Си++11 универсальный инициализатор int x{}; позволяет одним синтаксисом создать объект, структуру, массив. В Си++17 уточнено: если вместо типа стоит auto — пользователь хочет создать один объект и никаких initializer_list не нужно.

При этом auto x = {1, 2, 3}; продолжает создавать: с одной стороны, для совместимости с for (auto x : {1, 2, 3}), с другой — для одного объекта есть auto x = 1;[33][10].

auto x1 = { 3 };   // std::initializer_list<int>
auto x2 { 1, 2 };  // теперь ошибка
auto x3 { 3 };     // int

Структурное связывание[править | править код]

Появился новый способ объявления переменных для распаковки сложных объектов, который получил название структурного связывания[34].

Запись namespace A::B[править | править код]

Определение вложенных пространств имён:[10][35] namespace A::B {} как сокращение для namespace A { namespace B {} };

Аннотации для пространств имён и элементов перечисляемого типа[править | править код]

Например:

enum class TriBool {
  NO,
  MAYBE,
  YES,
  NN [[maybe_unused]],
  UNSPECIFIED [[deprecated("Переименован в MAYBE")]] = MAYBE
};
constexpr int TriBool_N = static_cast<int>(TriBool::NN);
const char* triBoolNames[TriBool_N] = { "no", "maybe", "yes" };

Какой-то заявленной цели пока нет[18][36], но это позволит разработчикам компиляторов придумать таковую — например, объявить, что элемент NN особый и его не надо присваивать переменным, обрабатывать в switch.

If при компиляции[править | править код]

Концепция SFINAE позволила сделать несложный шаблон enable_if, который обеспечивает разную функциональность для разных типов, но даёт тяжеловесный код. В Си++17 можно упростить программу: оператор if constexpr(expression) инстанцирует код, если выражение в скобках истинно[37].

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;
}

В данном случае мы убеждаемся, что разница между дробными числами невелика, а целые просто проверяем на равенство.

Упрощённый синтаксис двухместной операции в переменных шаблонах[править | править код]

Упакованные выражения[18][38]:

template<typename... As> bool foo(As... args)
    { return (args && ...); }

Шестнадцатеричное представление дробных чисел[править | править код]

Шестнадцатеричная мантисса и десятичный порядок: 0xC.68p+2, 0x1.P-126, аналогично подстановке %a. Си поддерживает этот синтаксис с версии 99[39].

Инициализация локальной переменной в if/switch[править | править код]

Аналогично инициализации локальных переменных в for, делает код компактнее[40].

if (auto it = m.find(key); it != m.end()) 
    return it->second;

Using в атрибутах[править | править код]

// Было
void f() {
    [[rpr::kernel, rpr::target(cpu,gpu)]] // повтор
    do-task();
}

// Стало
void f() {
    [[using rpr: kernel, target(cpu,gpu)]]
    do-task();
}

Бестиповые параметры в шаблонах[править | править код]

Позволяют задавать шаблонные параметры любого типа, через auto[41].

template<auto X> struct B { static constexpr auto value = X; };
B<5> b1;   // OK: template parameter type is int
B<'a'> b2; // OK: template parameter type is char
B<2.5> b3; // error: template parameter type cannot be double

Захват лямбда-объектом *this[править | править код]

Было: [self = *this]{ self.f(); }. Стало: [*this]{ f(); }[42].

Можно инициализировать enum class числом[править | править код]

enum class иногда применяется, чтобы сделать другой целый тип, не совместимый ни с чем. Теперь переменные этого типа можно инициализировать числами[43]

enum class Handle : intptr_t { INVALID = 0 };
Handle h { 42 };

Библиотека[править | править код]

Мелкие доработки библиотеки[править | править код]

  • Неконстантная перегрузка string::data. Используется для вызова низкоуровневых строковых функций, которые принимают участок памяти определённой длины и заполняют его символами (например, WinAPI). До Си++11 использовался const_cast<char*>(x.data()), до Си++17 — &x.front().
  • emplace_back одного элемента возвращает ссылку. Позволяет написать такую конструкцию:
v.emplace_back("alpha", "bravo").doSomething();
  • Стандартную библиотеку Си обновили с C99 до C11[44].
  • Функции std::size(x), std::begin(x), std::end(x), std::empty(x). Позволяют писать общий шаблонный код для контейнеров STL и массивов[25][45]. К тому же std::size — нужная функция, которую ранее часто писали своими силами с ошибками.
  • Добавлена частичная специализация bool_constant<bool B> = integral_constant<bool, B>;[46]
  • Добавились функции-свойства для SFINAE: is_swappable, is_nothrow_swappable, is_swappable_with, is_nothrow_swappable_with, is_aggregate (составной тип), has_unique_object_representations (тривиально копируемый объект, и любые два объекта с одинаковым значением имеют одинаковое внутреннее представление).
  • Расширена библиотека работы с неинициализированной памятью. Появились функции uninitialized_default_construct, uninitialized_value_construct, uninitialized_move, destroy, destroy_at, а также их версии для n элементов.
  • Новый шаблон void_t<T> = void. Упрощает создание SFINAE-шаблонов, которые можно раскрыть, если тип T существует[47].
  • Для std::search добавилась версия с объектом-искателем. По умолчанию существуют три искателя: простейший, Бойер-Мур и Бойер-Мур-Хорспул.
  • Новая функция make_from_tuple инициализирует тип T данными из кортежа.
  • Новая константа atomic::is_always_lock_free определяет, является ли атомарная переменная неблокирующей.
  • В chrono добавили функции округления вверх, вниз и до ближайшего.
  • В map/set добавили функции переброски (merge) и извлечения (extract) элементов.
  • Добавился тип shared_ptr<T>::weak_type = weak_ptr<T>.
  • В некоторых случаях аллокаторы могут иметь неполный тип. Теперь возможны рекурсивные структуры наподобие struct X { std::vector<X> data; };. Крупные компиляторы давно поддерживают такое, осталось только заспецифицировать.
  • Добавились неявные конструкторы в pair и tuple.
  • unique_ptr/shared_ptr могут работать с массивами в стиле Си (shared_ptr<string[]>(new string[n])). В Си++14 требовалось протаскивать правильную функцию удаления (shared_ptr<string[]>(new string[n], default_delete<string[]>() )).
  • Уточнена работа common_type[48][49].

Новый тип std::string_view[править | править код]

Часто бывает нужно передать неизменную строку в другой участок кода, это можно сделать такими методами:

void doSmth(const char *s);
void doSmth(const std::string &s);

Если методика владения памятью другая, приходится проводить преобразование.

В C++17 появился тип string_view — строка, имеющая только указатель и длину, без владения, управления памятью и даже без завершающего нуля — и поэтому она не имеет функции c_str(). Задача программиста — сделать, чтобы объект не пережил тот буфер памяти, где хранится строка, и передача параметров — отличное применение для него. Объект string_view очень маленький (2·битность машины), и его стоит передавать по значению, а не по ссылке.

Размер строки кэша[править | править код]

Есть две новые константы, 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 занимает одну строку кэша.

Теоретически обе константы должны быть одинаковыми, но для поддержки неоднородных архитектур решено было сделать две константы.[50]

Новый тип shared_mutex[править | править код]

Мьютекс, позволяющий читать параллельно и писать одному[51]. Блокировщики для него называются shared_lock и unique_lock.

Автоматическое определение типа параметра контейнера[править | править код]

В библиотеке появились функции, так называемые 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

Новые функции вставки в ассоциативный массив с неповторяющимся ключом[править | править код]

Для std::map и std::unordered_map добавились две новых функции[52].

#include <iostream>
#include <map>

class Pair {
public:
    int value1, value2;
    Pair() : value1(0), value2(0) {}
    explicit Pair(int aValue1) : value1(aValue1), value2(0) {}
    Pair(int aValue1, int aValue2)
        : value1(aValue1), value2(aValue2) {}
};

int main()
{
    std::map<std::string, Pair> m;

    // C++11
    m["a"] = Pair(3, 4);
    m.emplace("a", 1);       // Pair создаётся всегда

    // C++17
    m.insert_or_assign("a", Pair(3, 4));
    m.try_emplace("a", 1);   // Pair создаётся когда надо

    return 0;
}

Новые математические функции[править | править код]

Внесены в пространство имён std нестандартные математические функции: beta, cyl_bessel_i/j/k, cyl_neumann, [comp_]ellint_1/2/3, expint, hermite, [assoc_]laguerre, [assoc_]legendre, riemann_zeta, sph_bessel, sph_legendre, sph_neumann[53][54]. За пределами std (в math.h) их нет.

Из первого предложения (2010): «Мы надеемся, что принятие этого предложения даст посыл разным сообществам вычислителей, что, несмотря на расхожее поверье, Си++ тоже вполне годится для их отрасли». Тогда его не приняли. Сейчас основные производители библиотек (Dinkumware, Boost, GCC) уже имеют эти функции.

Также добавились вычисление НОД[55] и НОК[56], функция приведения в диапазон (clamp)[57], трёхмерная гипотенуза hypot(x, y, z).

Библиотека файловой системы[править | править код]

Библиотека файловой системы, основанная на boost::filesystem, позволяет:[58]

  • автоматическую интернационализацию имён файлов в зависимости от особенностей ОС. Библиотека скрывает, в какой кодировке она работает, и сама конвертирует имена в нужную — как минимум в определённую локалью однобайтовую и различные варианты Юникода;
  • проход по каталогам (в том числе рекурсивный);
  • определение типов файлов (обычный, каталог, сокет…);
  • деление пути к файлу на составные части: диск, каталог, имя и расширение;
  • создание каталогов, копирование файлов, удаление каталогов и файлов (в том числе рекурсивное);
  • получение имён для временных файлов.

Вариативные типы[править | править код]

Появился класс std::any, способный содержать данные любого типа[59][60]. От реализаций требуется, чтобы небольшие объекты помещались в any без выделения памяти. Функция any_cast требует точного совпадения типа, и any_cast<double> ничего не даст, если внутри объекта int.

std::cout << std::boolalpha;
std::any a = 1;
std::cout << a.type().name() << ": " << std::any_cast<int>(a) << std::endl;
a = 3.14;
std::cout << a.type().name() << ": " << std::any_cast<double>(a) << std::endl;
a = true;
std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << std::endl;

// i: 1
// d: 3.14
// b: true

Также есть более простые std::variant<int, bool, double> и std::optional<T>.

Низкоуровневые функции преобразования число-текст[править | править код]

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

Теперь есть встроенные локаленезависимые сверхскоростные from_chars[61] и to_chars[62]. Устроены они так, что не требуют (и не производят) закрывающего нуля и могут работать, например, на string_view. Из-за ограниченности и локаленезависимости предназначены они в первую очередь для JSON и XML, где нужна огромная скорость.

Новый тип polymorphic_allocator[править | править код]

Структуры данных STL (строки, вектора и прочее) содержат шаблонный параметр — аллокатор памяти. Этот аллокатор работает как концепция обобщённого программирования, а не как интерфейс объектно-ориентированного: выделение памяти в куче и пуле даёт разные несовместимые типы. Класс polymorphic_allocator — стандартное начало для редкой задачи: в зависимости от каких-то условий, выделять память то в куче, то в пуле.

Сам по себе polymorphic_allocator — не интерфейс, но он связан с интерфейсом memory_resource.

Новый шаблон std::invoke[править | править код]

Позволяет единообразно вызывать функции, объекты с операцией () (функторы) и лямбда-объекты[63]. Также добавились функции is_invocable, is_invocable_r, invoke_result.

См. также[править | править код]

Ссылки[править | править код]

  • Черновик стандарта, N4659, от 21.03.2017

Примечания[править | править код]

  1. ISO/IEC 14882:2017.
  2. Recent milestones: C++17 nearly feature-complete, second round of TSes now under development
  3. The Parallelism TS Should be Standardized.
  4. N3981: Removing trigraphs??! (Richard Smith) (6 мая 2014).
  5. IBM comment on preparing for a Trigraph-adverse future in C++17, IBM paper N4210, 2014-10-10.
  6. Remove Deprecated Use of the register Keyword.
  7. Remove Deprecated operator++(bool).
  8. Removing Deprecated Exception Specifications from C++17.
  9. N4190: Removing auto_ptr, random_shuffle(), And Old <functional> Stuff (Stephan T. Lavavej).
  10. 1 2 3 Updates to my trip report.
  11. Remove Deprecated iostreams aliases.
  12. Removing Allocator Support in std::function (rev 1).
  13. Deprecating Vestigial Library Parts in C++17.
  14. Deprecating <codecvt>.
  15. Proposed Resolution for CA 14 (shared_ptr use_count/unique).
  16. Resolving GB 55, US 84, US 85, US 86.
  17. N4259: Wording for std::uncaught_exceptions (Herb Sutter).
  18. 1 2 3 4 5 New core language papers adopted for C++17.
  19. C++17 should refer to C11 instead of C99.
  20. Make exception specifications be part of the type system.
  21. Dynamic memory allocation for over-aligned data.
  22. [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf Refining Expression Evaluation Order for Idiomatic C++].
  23. N4268: Allow constant evaluation for all non-type template arguments (Richard Smith).
  24. Generalizing the Range-Based For Loop.
  25. 1 2 New standard library papers adopted for C++17.
  26. N4284: Contiguous Iterators (Jens Maurer).
  27. N4267: Adding u8 character literals (Richard Smith).
  28. Temporarily discourage memory_order_consume.
  29. The Purpose of memory_order_consume in C++11
  30. N3928: Extending static_assert, v2 (Walter E. Brown).
  31. Так, авторы PVS-Studio часто жаловались на ошибку: программист вместо clear() писал empty().
  32. N4051: Allow typename in a template template parameter (Richard Smith).
  33. N3922: New Rules for auto deduction from braced-init-list (James Dennett).
  34. Structured binding declaration (since C++17) en.cppreference.com
  35. N4230: Nested namespace definition (Robert Kawulak, Andrew Tomazos).
  36. N4266: Attributes for namespaces and enumerators (Richard Smith).
  37. constexpr if: A slightly different syntax.
  38. N4295: Folding expressions (Andrew Sutton, Richard Smith).
  39. Hexadecimal floating literals for C++.
  40. Selection statements with initializer.
  41. Declaring non-type template parameters with auto.
  42. Lambda Capture of *this by Value as [=,*this].
  43. Construction Rules for enum class Values.
  44. C++17 should refer to C11 instead of C99.
  45. N4280: Non-member size() and more (Riccardo Marcangelo).
  46. Wording for bool_constant, revision 1
  47. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf
  48. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0435r1.pdf
  49. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0548r1.pdf
  50. P0154R1 constexpr std::hardware_{constructive,destructive}_interference_size.
  51. std::shared_mutex - cppreference.com
  52. Improved insertion interface for std::{unordered_,}map (revised)
  53. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3060.pdf
  54. Mathematical Special Functions for C++17, v5.
  55. std::gcd - cppreference.com
  56. std::lcm - cppreference.com
  57. std::clamp - cppreference.com
  58. Filesystem Library Proposal (Beman Dawes).
  59. C++ Extensions for Library Fundamentals, Version 2, Working Draft
  60. std::any - cppreference.com
  61. std::from_chars - cppreference.com
  62. std::to_chars - cppreference.com
  63. A proposal to add invoke function template (Revision 1)