КулЛиб - Классная библиотека! Скачать книги бесплатно 

Тур по С++. Краткий курс [Бьерн Страуструп] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
Поддержи переводчика

Содержание
Содержание ................................................................................................................................................................. 2
Тур по C++ .................................................................................................................................................................... 9
Благодарность ............................................................................................................................................... 11
Основы .................................................................................................................................................................... 12
1.1 Введение .................................................................................................................................................... 12
1.2 Программы ............................................................................................................................................... 12
1.2.1 Hello, World! ................................................................................................................................ 13
1.3 Функции ..................................................................................................................................................... 15
1.4 Типы, Переменные и Арифметика ............................................................................................... 16
1.4.1 Арифметика ................................................................................................................................ 17
1.4.2 Инициализация......................................................................................................................... 19
1.5 Область видимости и время жизни ............................................................................................. 20
1.6 Константы ................................................................................................................................................ 21
1.7 Указатели, Массивы и Ссылки ....................................................................................................... 22
1.7.1 Нулевой указатель .................................................................................................................. 24
1.8 Условные операторы .......................................................................................................................... 25
1.9 Сопоставление с аппаратным обеспечением ......................................................................... 27
1.9.1 Присваивание ............................................................................................................................ 27
1.9.2 Инициализация......................................................................................................................... 28
1.10 Советы ...................................................................................................................................................... 29
Пользовательские типы ................................................................................................................................ 31
2.1 Введение .................................................................................................................................................... 31
2.2 Структуры ................................................................................................................................................. 32
2.3 Классы......................................................................................................................................................... 33
2.4 Перечисления ......................................................................................................................................... 35
2.5 Объединения ........................................................................................................................................... 36
2.6 Советы ........................................................................................................................................................ 38
Модульность........................................................................................................................................................ 39
3.1 Введение .................................................................................................................................................... 39
3.2 Раздельная компиляция ................................................................................................................... 40
3.2.1 Заголовочные файлы ............................................................................................................. 40
3.2.2 Модули .......................................................................................................................................... 42
3.3 Пространства имён .............................................................................................................................. 45
3.4 Аргументы функции и возвращаемые значения ................................................................. 47
3.4.1 Передача аргументов ............................................................................................................. 47
3.4.2 Возвращение значений......................................................................................................... 48
3.4.3 Выведение типа возвращаемого значения ................................................................ 50
3.4.4 Суффиксная запись типа возвращаемого значения .............................................. 50
3.4.5 Структурное связывание ..................................................................................................... 50
3.5 Советы ........................................................................................................................................................ 52
Обработка ошибок............................................................................................................................................ 53
4.1 Введение .................................................................................................................................................... 53
4.2 Исключения ............................................................................................................................................. 54
4.3 Инварианты ............................................................................................................................................. 55
4.4 Альтернативные способы обработки ошибок ...................................................................... 57
2

4.5 Утверждения ........................................................................................................................................... 59
4.5.1 assert()................................................................................................................................................ 60
4.5.2 static_assert() ................................................................................................................................... 60
4.5.3 noexcept ............................................................................................................................................. 61
4.6 Советы ........................................................................................................................................................ 61
Классы .................................................................................................................................................................... 63
5.1 Введение ................................................................................................................................................... 63
5.1.1 Классы ........................................................................................................................................... 64
5.2 Конкретные типы ................................................................................................................................ 64
5.2.1 Арифметические типы ......................................................................................................... 65
5.2.2 Контейнеры ............................................................................................................................... 67
5.2.3 Инициализация контейнеров ........................................................................................... 68
5.3 Абстрактные типы............................................................................................................................... 70
5.4 Виртуальные функции ...................................................................................................................... 73
5.5 Иерархии классов ................................................................................................................................. 73
5.5.1 Преимущества иерархий ..................................................................................................... 76
5.5.2 Навигация в иерархии .......................................................................................................... 77
5.5.3 Предотвращение утечки ресурсов ................................................................................. 78
5.6 Советы ........................................................................................................................................................ 79
Основные операции ........................................................................................................................................ 81
6.1 Введение ................................................................................................................................................... 81
6.1.1 Основные операции ............................................................................................................... 81
6.1.2 Преобразования типов ......................................................................................................... 83
6.1.3 Инициализация элементов ................................................................................................ 84
6.2 Копирование и перемещение ........................................................................................................ 84
6.2.1 Копирование контейнеров ................................................................................................ 84
6.2.2 Перемещение контейнеров ............................................................................................... 86
6.3 Управление ресурсами ...................................................................................................................... 88
6.4 Перегрузка операторов ..................................................................................................................... 90
6.5 Стандартные операции ..................................................................................................................... 91
6.5.1 Операторы сравнения........................................................................................................... 91
6.5.2 Операции с контейнерами.................................................................................................. 92
6.5.3 Итераторы и “умные указатели” ..................................................................................... 93
6.5.4 Операции ввода-вывода ...................................................................................................... 93
6.5.5 swap()............................................................................................................................................. 94
6.5.6 hash ........................................................................................................................................... 94
6.6 Пользовательские литералы ......................................................................................................... 94
6.7 Советы ........................................................................................................................................................ 95
Шаблоны ............................................................................................................................................................... 97
7.1 Введение ................................................................................................................................................... 97
7.2 Параметризованные типы .............................................................................................................. 97
7.2.1 Ограниченные аргументы шаблона .............................................................................. 99
7.2.2 Аргументы-значения шаблона ......................................................................................100
7.2.3 Выведение типов аргументов шаблонов..................................................................101
7.3 Параметризированные операции ..............................................................................................102
7.3.1 Шаблоны функций................................................................................................................103
7.3.2 Функциональные объекты ..............................................................................................103
3

7.3.3 Лямбда выражения .............................................................................................................. 105
7.3.3.1 Лямбды как аргументы функции ................................................................................ 105
7.3.3.2 Лямбды для инициализации ...................................................................................... 106
7.3.3.3 Напоследок, finally() .................................................................................................... 108

7.4 Механизмы шаблонов ..................................................................................................................... 108
7.4.1 Шаблоны переменных ....................................................................................................... 109
7.4.2 Псевдонимы ............................................................................................................................. 109
7.4.3 if времени компиляции...................................................................................................... 110
7.5 Советы ..................................................................................................................................................... 111
Концепты и обобщенное программирование ................................................................................ 113
8.1 Введение ................................................................................................................................................. 113
8.2 Концепты ............................................................................................................................................... 114
8.2.1 Использование концептов ............................................................................................... 114
8.2.2 Перегрузка основанная на концептах ....................................................................... 116
8.2.3 Правильный код .................................................................................................................... 117
8.2.4 Определение концептов ................................................................................................... 117
8.2.4.1 Проверка определения ............................................................................................... 120

8.2.5 Концепты и auto .................................................................................................................... 120
8.2.6 Концепты и типы .................................................................................................................. 121
8.3 Обобщённое программирование ............................................................................................... 122
8.3.1 Использование концептов ............................................................................................... 122
8.3.2 Абстракции использующие шаблоны........................................................................ 123
8.4 Шаблоны с переменным числом аргументов ..................................................................... 124
8.4.1 Выражения свёртки............................................................................................................. 125
8.4.2 Передача аргументов .......................................................................................................... 126
8.5 Модель компиляции шаблонов .................................................................................................. 127
8.6 Советы ..................................................................................................................................................... 128
Обзор стандартной библиотеки ............................................................................................................ 129
9.1 Введение ................................................................................................................................................. 129
9.2 Компоненты стандартной библиотеки ................................................................................. 130
9.3 Организация стандартной библиотеки ................................................................................. 131
9.3.1 Пространства имён .............................................................................................................. 131
9.3.2 Пространство имён ranges .............................................................................................. 132
9.3.3 Модули ....................................................................................................................................... 133
9.3.4 Заголовочные файлы .......................................................................................................... 133
9.4 Советы ..................................................................................................................................................... 134
Строки и регулярные выражения ......................................................................................................... 135
10.1 Введение .............................................................................................................................................. 135
10.2 Строки ................................................................................................................................................... 135
10.2.1 Реализация string................................................................................................................ 137
10.3 Строковые представления ......................................................................................................... 138
10.4 Регулярные выражения ............................................................................................................... 139
10.4.1 Поиск ........................................................................................................................................ 140
10.4.2 Описание регулярных выражений ........................................................................... 141
10.4.3 Итераторы ............................................................................................................................. 145
10.5 Советы ................................................................................................................................................... 145
4

Ввод и вывод .....................................................................................................................................................148
11.1 Введение ...............................................................................................................................................148
11.2 Вывод .....................................................................................................................................................149
11.3 Ввод .........................................................................................................................................................150
11.4 Состояния потоков I/O..................................................................................................................151
11.5 I/O пользовательских типов ......................................................................................................152
11.6 Форматирование вывода.............................................................................................................153
11.6.1 Форматирование потока ................................................................................................153
11.6.2 Форматирование в стиле printf() .................................................................................155
11.7 Потоки ...................................................................................................................................................157
11.7.1 Стандартные потоки.........................................................................................................157
11.7.2 Файловые потоки ...............................................................................................................158
11.7.3 Строковые потоки ..............................................................................................................158
11.7.4 Потоки памяти .....................................................................................................................159
11.7.5 Синхронизированные потоки ......................................................................................159
11.8 I/O в стиле Си .....................................................................................................................................160
11.9 Файловая система............................................................................................................................160
11.9.1 Пути ...........................................................................................................................................161
11.9.2 Файлы и каталоги ..............................................................................................................163
11.10 Советы .................................................................................................................................................164
Контейнеры.......................................................................................................................................................166
12.1 Введение ...............................................................................................................................................166
12.2 vector ......................................................................................................................................................166
12.2.1 Элементы ................................................................................................................................169
12.2.2 Проверка диапазона ..........................................................................................................169
12.3 list .............................................................................................................................................................171
12.5 map ..........................................................................................................................................................173
12.6 unordered_map..................................................................................................................................174
12.7 Аллокаторы ........................................................................................................................................175
12.8 Обзор контейнеров .........................................................................................................................177
12.9 Советы ...................................................................................................................................................178
Алгоритмы .........................................................................................................................................................181
13.1 Введение ...............................................................................................................................................181
13.2 Применение итераторов ..............................................................................................................183
13.3 Типы итераторов .............................................................................................................................185
13.3.1 Потоковые итераторы .....................................................................................................186
13.4 Использование предикатов........................................................................................................188
13.5 Обзор алгоритмов ...........................................................................................................................189
13.6 Параллельные алгоритмы ..........................................................................................................190
13.7 Советы ...................................................................................................................................................191
Диапазоны..........................................................................................................................................................192
14.1 Введение ...............................................................................................................................................192
14.2 Представления ..................................................................................................................................193
14.3 Генераторы .........................................................................................................................................195
14.4 Конвейеры ...........................................................................................................................................195
14.5 Обзор концептов ..............................................................................................................................196
14.5.1 Концепты типов ..................................................................................................................197
5

14.5.2 Концепты итераторов ..................................................................................................... 199
14.5.3 Концепты диапазонов ..................................................................................................... 200
14.6 Советы ................................................................................................................................................... 201
Умные указатели и контейнеры ............................................................................................................ 202
15.1 Введение .............................................................................................................................................. 202
15.2 Указатели ............................................................................................................................................ 203
15.2.1 unique_ptr и shared_ptr.................................................................................................... 204
15.2.2 span ........................................................................................................................................... 207
15.3 Контейнеры ....................................................................................................................................... 208
15.3.1 array ........................................................................................................................................... 209
15.3.2 bitset .......................................................................................................................................... 211
15.3.3 pair .............................................................................................................................................. 212
15.3.4 tuple ........................................................................................................................................... 214
15.4 Альтернативы ................................................................................................................................... 215
15.4.1 variant ....................................................................................................................................... 215
15.4.2 optional ..................................................................................................................................... 217
15.4.3 any .............................................................................................................................................. 218
15.5 Советы ................................................................................................................................................... 218
Утилиты .............................................................................................................................................................. 220
16.1 Введение .............................................................................................................................................. 220
16.2 Время ..................................................................................................................................................... 220
16.2.1 Часы ........................................................................................................................................... 221
16.2.2 Календари .............................................................................................................................. 221
16.2.3 Временные зоны ................................................................................................................. 222
16.3 Адаптация функций ....................................................................................................................... 223
16.3.1 Лямбды как адапторы ..................................................................................................... 223
16.3.2 mem_fn() ................................................................................................................................. 223
16.3.3 function ..................................................................................................................................... 224
16.4 Функция типа .................................................................................................................................... 224
16.4.1 Предикаты типа .................................................................................................................. 225
16.4.2 Условные свойства ............................................................................................................ 227
16.4.3 Генераторы типов.............................................................................................................. 228
16.4.4 Связанные типы ................................................................................................................. 228
16.5 source_location ................................................................................................................................. 229
16.6 move() and forward() ....................................................................................................................... 229
16.7 Битовые манипуляции ................................................................................................................. 231
16.8 Выход из программы ..................................................................................................................... 232
16.9 Советы ................................................................................................................................................... 232
Числовые вычисления ................................................................................................................................ 234
17.1 Введение .............................................................................................................................................. 234
17.2 Математические функции .......................................................................................................... 235
17.3 Численные алгоритмы ................................................................................................................. 236
17.3.1 Многопоточные численные алгоритмы ................................................................ 236
17.4 Комплексные числа ....................................................................................................................... 237
17.5 Случайные числа ............................................................................................................................. 238
17.6 Векторная арифметика ................................................................................................................ 240
17.7 Числовые ограничения ................................................................................................................ 240
6

17.8 Псевдонимы типов..........................................................................................................................240
17.9 Математические константы ......................................................................................................241
17.10 Советы .................................................................................................................................................241
Параллелизм .....................................................................................................................................................243
18.1 Введение ...............................................................................................................................................243
18.2 Задачи и thread..................................................................................................................................244
18.2.1 Передача аргументов .......................................................................................................245
18.2.2 Возвращение результатов .............................................................................................246
18.3 Обмен данными ................................................................................................................................247
18.3.1 mutex и блокировки ..........................................................................................................247
18.3.2 atomic ........................................................................................................................................248
18.4 Ожидание событий .........................................................................................................................249
18.5 Коммуникации задач .....................................................................................................................250
18.5.1 future и promise ....................................................................................................................251
18.5.2 packaged_task ......................................................................................................................252
18.5.3 async().......................................................................................................................................253
18.5.4 Остановка thread .................................................................................................................254
18.6 Корутины (сопрограммы) ...........................................................................................................255
18.6.1 Кооперативная многозадачность ..............................................................................256
18.7 Советы ...................................................................................................................................................259
История и совместимость ..........................................................................................................................261
19.1 История .................................................................................................................................................261
19.1.1 Временная шкала................................................................................................................262
19.1.2 Ранние годы ..........................................................................................................................263
19.1.3 Стандарты ISO C++ .............................................................................................................266
19.1.4 Стандарты и стиль .............................................................................................................268
19.1.5 Использование C++ ............................................................................................................269
19.1.6 Модель C++ .............................................................................................................................269
19.2 Эволюция функций C++ ................................................................................................................270
19.2.1 Языковые особенности C++11 .....................................................................................270
19.2.2 Языковые особенности C++14 .....................................................................................271
19.2.3 Языковые особенности C++17 .....................................................................................271
19.2.4 Языковые особенности C++20 .....................................................................................272
19.2.5 Компоненты стандартной библиотеки C++11 ....................................................272
19.2.6 Компоненты стандартной библиотеки C++14 ....................................................273
19.2.7 Компоненты стандартной библиотеки C++17 ....................................................273
19.2.8 Компоненты стандартной библиотеки C++20 ....................................................273
19.2.9 Удаленные и устаревшие функции ...........................................................................274
19.3 Совместимость C/C++ ....................................................................................................................275
19.3.1 C и C++ - родные братья ..................................................................................................275
19.3.2 Проблемы совместимости .............................................................................................277
19.3.2.1 Проблемы стиля ......................................................................................................... 277
19.3.2.2 void* ............................................................................................................................. 278
19.3.2.3 Линковка ..................................................................................................................... 279

19.4 Библиография ...................................................................................................................................279
19.5 Советы ...................................................................................................................................................282
7

Модуль std.......................................................................................................................................................... 284
A.1 Введение ................................................................................................................................................ 284
A.2 Используйте то, что предлагает Ваша реализация ......................................................... 285
A.3 Используйте заголовки .................................................................................................................. 285
A.4 Сделайте свой собственный module std ................................................................................ 285
A.5 Советы ..................................................................................................................................................... 286
Index ...................................................................................................................................................................... 287

8

Тур по C++
Третье издание
Bjarne Stroustrup

Coverphoto by: Marco Pregnolato (Unsplash.com: @marco_pregnolato).
Author photo courtesy of Bjarne Stroustrup.
This book was typeset in Times and Helvetica by the author.
ISBN-13: 978-0-13-681648-5
ISBN-10: 0-13-681648-7
First printing, October 2022

9

Предисловие
Когда вы захотите проинструктировать,
будьте кратки.
– Цицерон

C++ ощущается как новый язык. Cегодня я могу выражать свои идеи чётче, проще и
непосредственнее, чем в C++98 или C++11. Кроме того, в результате программы лучше
проверяются компилятором и быстрее работают.
В этой книге дается обзор C++, как он определен в C++20, текущем стандарте ISO
C++, и реализован основными поставщиками C++. Кроме того, в ней упоминается пара
библиотечных компонентов, используемых в настоящее время, но не планируемых к
включению в стандарт до C++23.
Как и другие современные языки, C++ велик, и для эффективного использования
требуется большое количество библиотек. Цель этой небольшой книги - дать опытному программисту представление о том, что представляет собой современный C++.
Книга охватывает большинство основных языковых функций и основные компоненты
стандартной библиотеки. Эту книгу можно прочитать всего за день или два, но, очевидно, что требуется гораздо больше времени, чтобы научиться писать хороший код на
C++. Однако цель здесь не в овладении мастерством, а в том, чтобы дать обзор языка,
привести ключевые примеры и помочь программисту начать работу.
Предполагается, что вы уже программировали раньше. Если нет, пожалуйста, подумайте о прочтении учебника например Программирование: принципы и практика использования C++ (Второе издание) [Stroustrup, 2014], прежде чем продолжать чтение
этой книги. Даже если вы программировали раньше, язык, который вы использовали,
или приложения, которые вы писали, могут сильно отличаться от стиля C++, представленного здесь.
Представьте себе обзорную экскурсию по какому-нибудь городу, например, Копенгагену или Нью-Йорку. Всего за несколько часов вам покажут основные достопримечательности, расскажут несколько историй и дадут несколько советов о том, что делать
дальше. После такой экскурсии вы не знаете город. Вы не понимаете всего, что видели
и слышали; некоторые истории могут показаться странными или даже неправдоподобными. Вы не знаете, как ориентироваться в формальных и неформальных правилах, которые управляют жизнью в городе. Чтобы по-настоящему узнать город, нужно пожить
в нем, часто несколько лет. Однако, если вам немного повезет, вы получите общее
представление о том, что особенного в городе, и идеи о том, что может вас заинтересовать. После экскурсии может начаться настоящее исследование города.
В этой экскурсии представлены основные возможности языка C++, поддерживаемые
ими стили программирования, такие как объектно-ориентированное и обобщённое
программирование. В нем не делается попытка предоставить подробное, похожее на
справочник руководство, представление языка по отдельным функциям. В лучших традициях учебников я пытаюсь объяснить ту или иную функцию перед ее использованием, но это не всегда возможно, и не все читают текст строго последовательно. Я
предполагаю некоторую техническую зрелость от моих читателей. Итак, читателю рекомендуется использовать перекрестные ссылки и алфавитный указатель.
10

Аналогично, стандартные библиотеки представлены в виде примеров, а не в виде
полного описания. Читателю рекомендуется искать дополнительные и вспомогательные материалы по мере необходимости. Экосистема C++ - это гораздо больше, чем просто возможности, предлагаемые стандартом ISO (например, библиотеки, системы
сборки, инструменты анализа и среды разработки). В Интернете доступно огромное
количество материалов (разного качества). Большинство читателей найдут полезные
обучающие и обзорные видеоролики с конференций CppCon и Meeting C++. Для получения технических подробностей о языке и библиотеке, предлагаемых стандартом ISO
C++, я рекомендую [Cppreference]. Например, когда я упоминаю функцию или класс
стандартной библиотеки, можно легко найти их определение в других источниках, а
изучив документацию по ним, можно найти множество связанных с ними средств.
В этом туре C++ представлен как единое целое, а не как слоеный пирог. Следовательно, я редко идентифицирую языковые функции как присутствующие в C, C++98
или более поздних стандартах ISO. Такую информацию можно найти в главе 19 (История и совместимость). Я сосредотачиваюсь на основах и стараюсь быть кратким, но я
не полностью устоял перед искушением представить новые функции, такие как модули (§3.2.2), концепты (§8.2) и корутины (§18.6). Небольшое предпочтение свежим
разработкам также, по-видимому, удовлетворяет любопытство многих читателей, которые уже знакомы с какой-либо более старой версией C++.
Справочное руководство по языку программирования или стандарт просто указывают, что можно сделать с помощью языка, но программисты часто больше заинтересованы в том, чтобы научиться хорошо использовать язык программирования. Этот аспект частично рассматривается в ряде затронутых тем, частично в тексте и, в частности, в разделах с рекомендациями. Дополнительные рекомендации о том, что собой
представляет хороший современный C++, можно найти в руководстве C++ Core Guidelines [Stroustrup, 2015]. Core Guidelines могут стать хорошим источником для дальнейшего изучения идей, представленных в этой книге. Вы можете отметить поразительное сходство формулировок рекомендаций и даже нумерации рекомендаций между
Core Guidelines и этой книгой. Одна из причин заключается в том, что первое издание A
Tour of C++ было основным источником первоначальных Core Guidelines.

Благодарность
Спасибо всем, кто помогал дополнять и исправлять предыдущие выпуски “Экскурсии по C++”, особенно студентам моего курса "Проектирование с использованием C++"
в Колумбийском университете. Спасибо Morgan Stanley за то, что дали мне время написать это третье издание. Спасибо Чаку Эллисону, Гаю Дэвидсону, Стивену Дьюхерсту,
Кейт Грегори, Дэнни Калеву, Гору Нишанову и Дж.Си ван Винкелю за рецензию на
книгу и предложения по многим улучшениям.
Эта книга была создана автором с использованием troff с использованием макросов
Брайана Кернигана.
Manhattan, New York
Bjarne Stroustrup

11

1

Основы
Первое, что мы сделаем, давайте
убьем всех юристов – Генрих VI,
Part II



Введение



Программы

Hello, World!


Функции



Типы, Переменные и Арифметика

Арифметика; Инициализация


Область видимости и время жизни



Константы

 Указатели, Массивы и Ссылки
Нулевой указатель


Условные операторы



Сопоставление с аппаратным обеспечением

Присвоение; Инициализация


Советы

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

1.2 Программы
C++ - это компилируемый язык. Для запуска программы ее исходный текст должен
быть обработан компилятором, создающим объектные файлы, которые объединяются
компоновщиком, получая исполняемую программу. Программа на C++ обычно состоит
из множества файлов исходного кода (обычно называемых просто исходными файлами).

12

Исполняемая программа создается для определенной комбинации аппаратной платформы и операционной системы; она не переносима, скажем, с устройства Android на
персональный компьютер с Windows. Когда мы говорим о переносимости программ на
C++, мы обычно имеем в виду переносимость исходного кода; то есть исходный код может быть успешно скомпилирован и запущен в различных системах.
Стандарт ISO C++ определяет два типа сущностей:


Фундаментальные возможности языка, такие как встроенные типы (например, char и int) и циклы (например, операторы for и while)



Компоненты стандартной библиотеки STL, такие как контейнеры (например, vector и map) и операции ввода-вывода (например, . Например:
void f(Vector v, Vector& rv, Vector* pv)
{
int i1 = v.sz;
// access through name
int i2 = rv.sz;
// access through reference
int i3 = pv->sz;
// access through pointer
}

2.3 Классы
Определение данных отдельно от операций над ними имеет преимущества, такие
как возможность использовать данные произвольными способами. Однако для того,
чтобы пользовательский тип обладал всеми свойствами, ожидаемыми от “реального
типа”, необходима более тесная связь между представлением данных и операциями
над ними. В частности, мы часто хотим сохранить представление данных недоступным
для пользователей, чтобы упростить использование, гарантировать согласованное использование данных и позволить нам позже улучшить представление данных. Чтобы
сделать это, мы должны различать интерфейс типа (который общедоступен) и его реализацию (которая имеет доступ к данным недоступным другими способами). Языковой механизм для этого называется классом. Класс состоит из набора членов, в котором могут быть элементы данных, функции или типы.
Интерфейс класса определяется его public публичными членами, а его private приватные члены доступны только через этот интерфейс. Публичная public и приватная private
части объявления класса могут отображаться в любом порядке, но обычно мы размещаем public объявления первыми, а private - позже, за исключением случаев, когда мы хотим подчеркнуть представление. Например:
class Vector {
public:
Vector(int s) :elem{new double[s]}, sz{s} { }
double& operator[](int i) { return elem[i]; }
int size() { return sz; }
private:
double* elem; // pointer to the elements
int sz;
// the number of elements
};

// construct a Vector
// element access: subscripting

Учитывая это, мы можем определить переменную нашего нового типа Vector:
33

Vector v(6);

// a Vector with 6 elements

Мы можем проиллюстрировать объект Vector графически:

По сути, объект Vector представляет собой “дескриптор”, содержащий указатель на
элементы (elem) и количество элементов (sz). Количество элементов (6 в примере) может варьироваться от одного объекта Vector к другому объекту Vector, и объект Vector может иметь разное количество элементов в разное время (§5.2.3). Однако сам объект Vector всегда имеет одинаковый размер. Это основной метод обработки различных объемов информации в C++: дескриптор фиксированного размера, ссылающийся на переменный объем данных “в другом месте” (например, в динамической памяти, выделенной new; §5.2.2). Как проектировать и использовать такие объекты - основная тема
главы 5.
Здесь представление Vector (элементы elem и sz) доступно только через интерфейс,
предоставляемый public элементами: Vector(), operator[](), и size(). Пример read_and_sum() из
§2.2 упрощается до:
double read_and_sum(int s)
{
Vector v(s);
for (int i=0; i!=v.size(); ++i)
cin>>v[i];
double sum = 0;
for (int i=0; i!=v.size(); ++i)
sum+=v[i];
return sum;

// make a vector of s elements
// read into elements

// take the sum of the elements

}

Функция-член с тем же именем, что и у её класса, называется конструктором, то
есть функцией, используемой для создания объектов класса. Таким образом, конструктор Vector() заменяет vector_init() из §2.2. В отличие от обычной функции, конструктор гарантированно используется для инициализации объектов своего класса. Таким образом, определение конструктора устраняет проблему неинициализированных переменных для класса.
Vector(int) определяет, как создаются объекты типа Vector. В частности, в нем указано,
что для этого ему нужно целое число. Это целое число используется в качестве количества элементов. Конструктор инициализирует члены объекта Vector, используя список
инициализаторов элементов:
:elem{new double[s]}, sz{s}

То есть сначала инициализируется elem указателем на s элементов типа double, полученных из кучи. Затем инициализируется sz значением s.
Доступ к элементам обеспечивается функцией индекса, называемой operator[]. Он возвращает ссылку на соответствующий элемент (double& позволяющий как чтение, так и
запись).
Функция size() возвращает количество хранящихся элементов.

34

Очевидно, что обработка ошибок здесь полностью отсутствует, но мы вернемся к
этому в главе 4. Аналогично, мы не предоставили механизм для “возврата” массива double, полученного с помощью new; в §5.2.2 показано, как определить деструктор, чтобы
элегантно сделать это.
Между struct и class нет принципиальной разницы; struct - это просто class, члены которого по умолчанию являются public. Например, вы можете определить конструкторы и
другие функции-члены для struct.

2.4 Перечисления
В дополнение к классам, C++ поддерживает простую форму пользовательского типа,
для которой мы можем перечислять значения:
enum class Color { red, blue, green };
enum class Traffic_light { green, yellow, red };
Color col = Color::red;
Traffic_light light = Traffic_light::red;

Обратите внимание, что перечислители (например, red) находятся в области видимости их enum class, так что их можно многократно использовать в разных enum class без
путаницы. Например, Color::red - это red класса Color, который отличается от Traffic_light::red.
Перечисления используются для представления небольших наборов целочисленных
значений. Перечисления нужны чтобы сделать код более читаемым и менее подверженным ошибкам, чем при использовании литералов, а не символических (и мнемонических) имён перечислителей.
class после enum указывает, что перечисление строго типизировано и что его перечислители ограничены пространством имён класса. Будучи отдельными типами, enum class
помогают предотвратить случайное неправильное использование констант. В частности, мы не можем смешивать значения Traffic_light и Color:
Color x1 = red;
Color y2 = Traffic_light::red;
Color z3 = Color::red;
auto x4 = Color::red;

//
//
//
//

error: which red?
error: that red is not a Color
OK
OK: Color::red is a Color

Аналогично, мы не можем неявно смешивать Color и целочисленные значения:
int i = Color::red;
Color c = 2;

// error: Color::red is not an int
// initialization error: 2 is not a Color

Перехват попыток преобразования в enum является хорошей защитой от ошибок, но
часто мы хотим инициализировать enum значением из его базового типа (по умолчанию это int), так что это разрешено, как и явное преобразование из базового типа:
Color x = Color{5};
Color y {6};

// OK, but verbose
// also OK

Аналогично, мы можем явно преобразовать значение enum в его базовый тип:
int x = int(Color::red);

По умолчанию в enum class определены присваивание, инициализация и сравнения
(например, == и t == Type::num)
cout i;

36

// a Type can hold values ptr and num (§2.4)

// ...
}

Члены p и i никогда не используются одновременно, поэтому память тратится впустую. Это можно легко исправить, указав, что оба должны быть членами union, например так:
union Value {
Node* p;
int i;
};

Теперь Value::p и Value::i помещаются по одному и тому же адресу памяти каждого объекта Value.
Такого рода оптимизация пространства может быть важна для приложений, которые занимают большие объемы памяти, поэтому компактное представление имеет решающее значение.
Язык не отслеживает, какое значение содержит union, поэтому программист должен
это сделать:
struct Entry {
string name;
Type t;
Value v;
// use v.p if t==Type::ptr; use v.i if t==Type::num
};
void f(Entry* pe)
{
if (pe->t == Type::num)
cout v.i;
// ...
}

Поддержание соответствия между полем типа, иногда называемым дискриминантом или тегом (здесь t), и типом, содержащимся в union, подвержено ошибкам. Чтобы
избежать ошибок, мы можем обеспечить соблюдение этого соответствия, инкапсулировав объединение и поле типа в класс и предложив доступ только через функциичлены, которые правильно используют объединение. На прикладном уровне абстракции, опирающиеся на такие объединения с тегами, являются распространенными и полезными. Использование “голых” union лучше свести к минимуму.
В стандартной библиотеке тип variant, может быть использован для устранения большинства прямых применений объединений. variant хранит одно значение из набора альтернативных типов (§15.4.1). Например, variant может содержать либо Node*,
либо int. Используя variant, пример Entry может быть записан как:
struct Entry {
string name;
variant v;
};
void f(Entry* pe)
{
if (holds_alternative(pe->v))
cout v);
// ...
}

// does *pe hold an int? (see §15.4.1)
// get the int

Для многих применений variant проще и безопаснее в использовании, чем union.
37

2.6 Советы
[1] Отдавайте предпочтение четко определенным пользовательским типам перед
встроенными типами, если встроенные типы слишком низкоуровневые; §2.1.
[2] Организуйте связанные данные в структуры (struct или class); §2.2; [CG: C.1].
[3] Понимайте различие между интерфейсом и реализацией, использующей class;
§2.3; [CG: C.3].
[4] struct - это просто class, члены которого по умолчанию являются public; §2.3.
[5] Определите конструкторы чтобы гарантировать и упростить инициализацию
классов class; §2.3; [CG: C.2].
[6] Используйте перечисления для представления наборов именованных констант;
§2.4; [CG: Enum.2].
[7] Предпочитайте class enum “простым” enum, чтобы свести к минимуму неожиданности; §2.4; [CG: Enum.3].
[8] Определите операции над перечислениями для безопасного и простого использования; §2.4; [CG: Enum.4].
[9] Избегайте “голых” union; оберните ихв класс вместе с полем типа §2.5; [CG: C.181].
[10] Предпочитайте std::variant “голым union”; §2.5.

38

3

Модульность
Занимайся своим делом.
– американская поговорка



Введение



Раздельная компиляция

Заголовочные файлы; Модули


Пространства имён

 Аргументы функции и возвращаемые значения
Передача аргументов; Возвращение значений; Выведение типа возвращаемого значения; Суффиксная запись типа возвращаемого значения; Структурное связывание


Советы

3.1 Введение
Программа на C++ состоит из множества отдельно разработанных частей, таких как
функции (§1.2.1), пользовательские типы (глава 2), иерархии классов (§5.5) и шаблоны
(глава 7). Ключом к управлению таким множеством частей является четкое определение взаимодействий между этими частями. Первым и наиболее важным шагом является разделение интерфейса к части и ее реализации. На уровне языка C++ интерфейсы
представлены с помощью объявлений. Объявление определяет все, что необходимо для
использования функции или типа. Например:
double sqrt(double);
//
ble
class Vector {
//
public:
Vector(int s);
double& operator[](int
int size();
private:
double* elem;
//
int sz;
};

the square root function takes a double and returns a douwhat is needed to use a Vector

i);

elem points to an array of sz doubles

Ключевым моментом здесь является то, что тела функций, определения функций,
могут находиться “в другом месте”. В этом примере мы могли бы пожелать, чтобы
представление Vector также было “в другом месте”, но мы разберемся с этим позже (говоря об абстрактных типах; §5.3). Определение sqrt() будет выглядеть следующим образом:
39

double sqrt(double d)
// definition of sqrt()
{
// ... algorithm as found in math textbook ...
}

Для Vector нам нужно определить все три функции-члена:
Vector::Vector(int s)
:elem{new double[s]}, sz{s}
{
}

// definition of the constructor
// initialize members

double& Vector::operator[](int i)
{
return elem[i];
}

// definition of subscripting

int Vector::size()
{
return sz;
}

// definition of size()

Мы должны определить функции Vector, но не sqrt(), потому что это часть стандартной библиотеки. Однако реальной разницы нет: библиотека - это просто “какой-то другой код, который мы используем при необходимости”, написанный с использованием
тех же языковых средств, которые используем мы.
Для объекта, такого как функция, может быть много объявлений, но только одно
определение.

3.2 Раздельная компиляция
C++ поддерживает концепцию раздельной компиляции, когда пользовательский код
видит только объявления используемых типов и функций. Это можно сделать двумя
способами:


Заголовочные файлы (§3.2.1): Поместите объявления в отдельные файлы,
называемые заголовочными файлами, и буквально #include(включите) заголовочный файл там, где необходимы его объявления.



Модули (§3.2.2): Определите файлы модулей module, скомпилируйте их отдельно и импортируйте import их при необходимости. Код, импортирующий
import модуль module, видит только явно экспортированные export объявления.
Любой из них может быть использован для организации программы в набор полунезависимых фрагментов кода. Такое разделение может быть использовано для минимизации времени компиляции и обеспечения разделения логически различных частей
программы (таким образом, сводя к минимуму вероятность ошибок). Библиотека часто
представляет собой набор отдельно скомпилированных фрагментов кода (например,
функций).
Метод организации кода с использованием заголовочных файлов восходит к самым
ранним дням Cи и до сих пор остается наиболее распространенным. Использование модулей является новым в C++20 и дает огромные преимущества в плане гигиены кода и
времени компиляции.

3.2.1 Заголовочные файлы

40

Традиционно мы помещаем объявления, указывающие интерфейс к фрагменту
кода, который мы рассматриваем как модуль, в файл с именем, указывающим на его
предполагаемое использование. Например:
// Vector.h:
class Vector {
public:
Vector(int s);
double& operator[](int i);
int size();
private:
double* elem;
// elem points to an array of sz doubles
int sz;
};

Это объявление размещено в файле Vector.h. Затем пользователи #include этот файл,
называемый заголовочным файлом, для доступа к этому интерфейсу. Например:
// user.cpp:
#include "Vector.h"
#include

// get Vector's interface
// get the standard-library math function
// interface including sqrt()

double sqrt_sum(const Vector& v)
{
double sum = 0;
for (int i=0; i!=v.size(); ++i)
sum+=std::sqrt(v[i]);
return sum;
}

// sum of square roots

Чтобы помочь компилятору обеспечить согласованность, файл .cpp, предоставляющий реализацию Vector, также будет включать файл .h, предоставляющий его интерфейс:
// Vector.cpp:
#include "Vector.h"

// get Vector's interface

Vector::Vector(int s)
:elem{new double[s]}, sz{s}
{
}

// initialize members

double& Vector::operator[](int i)
{
return elem[i];
}
int Vector::size()
{
return sz;
}

Код в user.cpp и Vector.cpp оба используют информацию об интерфейсе Vector, представленную в Vector.h, но в остальном эти два файла независимы и могут быть скомпилированы отдельно. Графически фрагменты программы можно представить следующим образом:
41

Наилучший подход к организации программы - представлять ее как набор модулей с
четко определенными зависимостями. Заголовочные файлы представляют эту модульность с помощью файлов, а затем используют эту модульность посредством раздельной компиляции.
Файл .cpp, который компилируется сам по себе (включая h-файлы, которые он
#include), называется единицей трансляции. Программа может состоять из тысяч единиц
трансляции.
Использование заголовочных файлов и #include - это очень старый способ имитации
модульности со значительными недостатками:


Время компиляции: Если вы #include header.h в 101 единицу трансляции, текст
header.h будет обработан компилятором 101 раз



Зависимость от порядка: Если мы включаем #include header1.h перед header2.h,
то объявления и макросы (§19.3.2.1) в header1.h могут повлиять на код в
header2.h. Если вместо этого вы включите #include header2.h перед header1.h, то
уже header2.h может повлиять на код в header1.h.



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



Транзитивность: весь код, необходимый для объявления в заголовочном
файле, должен присутствовать в этом заголовочном файле. Это приводит к
массовому раздуванию кода, поскольку заголовочные файлы #include в себя
другие заголовки, и это приводит к тому, что пользователь заголовочного
файла – случайно или намеренно – становится зависимым от таких деталей
реализации.
Очевидно, что это не идеально, и этот метод был основным источником трат времени на компиляцию и ошибок с тех пор, как он был впервые введен в C в начале 1970х годов. Однако использование заголовочных файлов было жизнеспособным на протяжении десятилетий, и старый код, использующий #include, будет “жить” очень долго, потому что обновление больших программ может быть дорогостоящим и отнимать много
времени.

3.2.2 Модули

42

В C++20 у нас наконец-то появился поддерживаемый языком способ прямого выражения модульности (§19.2.4). Рассмотрим, как выразить Vector и sqrt_sum() в примере из
§3.2 с использованием module:
export module Vector;

// defining the module called "Vector"

export class Vector {
public:
Vector(int s);
double& operator[](int i);
int size();
private:
double* elem;
// elem points to an array of sz doubles
int sz;
};

Vector::Vector(int s)
:elem{new double[s]}, sz{s}
{
}

// initialize members

double& Vector::operator[](int i)
{
return elem[i];
}
int Vector::size()
{
return sz;
}
export bool operator==(const Vector& v1, const Vector& v2)
{
if (v1.size() != v2.size())
return false;
for (int i = 0; i < v1.size(); ++i)
if (v1[i] != v2[i])
return false;
return true;
}

Здесь определён module с именем Vector, который экспортирует класс Vector, все его
функции-члены и функцию, не являющуюся членом, перегружающую оператор ==.
Способ, которым мы используем этот module, заключается в импорте import его туда,
где он нам нужен. Например:
// file user.cpp:
import Vector;
#include
sqrt()

// get Vector's interface
// get the standard-library math function interface including

double sqrt_sum(Vector& v)
{
double sum = 0;
for (int i=0; i!=v.size(); ++i)
sum+=std::sqrt(v[i]);
return sum;
}

// sum of square roots

43

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


Модуль компилируется только один раз (а не в каждой единице трансляции,
в которой он используется).



Два модуля могут быть importированы в любом порядке без изменения их
значения.



Если вы import или #include что-либо в модуль, пользователи вашего модуля
неявно получают доступ к этому (и не беспокоятся об этом): import не является транзитивным
Влияние модулей на обслуживаемость кода и производительность во время компиляции может быть впечатляющим. Например, по моим измерениям время компиляции
программы “Hello, World!”, с использованием
import std;

в 10 раз быстрее, чем версия, с использованием
#include

Удивительный результат, несмотря на то, что модуль std содержит всю стандартную
библиотеку, содержащую более чем в 10 раз больше информации, чем заголовок
. Причина в том, что модули экспортируют только интерфейсы, тогда как заголовок дает компилятору все, что он прямо или косвенно содержит. Это позволяет нам
использовать большие модули, так что нам не нужно запоминать, какой именно #include
из сбивающей с толку коллекции заголовков (§9.3.4). С этого момента я буду предполагать import std для всех примеров.
К сожалению, module std не является частью C++20. В приложении A объясняется, как
получить module std, если реализация стандартной библиотеки еще не предоставляет
его.
При определении модуля нам не обязательно разделять объявления и определения
в отдельные файлы; мы можем, если это улучшает организацию нашего исходного
кода, но это не обязательно. Мы могли бы определить простой Vector модуль следующим образом:
export module Vector;

// defining the module called "Vector"

export class Vector {
// ...
};
export bool operator==(const Vector& v1, const Vector& v2)
{
// ...
}

Графически фрагменты программы можно представить следующим образом:

44

Компилятор отделяет интерфейс модуля, указанный спецификаторами export, от деталей его реализации. Таким образом, интерфейс Vector генерируется компилятором и
никогда явно не именуется пользователем.
Используя module, нам не нужно усложнять наш код, чтобы скрыть детали реализации от пользователей; module будет предоставлять доступ только к exportированным
объявлениям. Рассмотрим:
export module vector_printer;
import std;
export
template
void print(std::vector& v)
// this is the (only) function seen by users
{
cout c2 && c2== '\"' ) {
// start with a { followed by a
"
string name;
// the default value of a string is the empty string:
""
while (is.get(c) && c!= '\"' )
// anything before a " is part of the

152

name
name+=c;
if (is>>c && c==',') {
int number = 0;
if (is>>number>>c && c=='}') { // read the number and a }
e = {name,number};
// assign to the entry
return is;
}
}
}
is.setstate(ios_base::failbit);
return is;

// register the failure in the stream

}

Операция ввода возвращает ссылку на свой istream, которую можно использовать для
проверки успешности выполнения операции. Например, использование в качестве
условия is>>c означает “Удалось ли нам прочитать char из is в c?”
По умолчанию is>>c пропускает пробельные символы, а is.get(c) этого не делает, поэтому этот оператор ввода Entry игнорирует (пропускает) пробелы вне строки имени,
но не внутри нее. Например:
{ "John Marwood Cleese", 123456
{"Michael Edward Palin", 987654}

}

Мы можем прочитать такую пару значений из входных данных в Entry, например, так:
for (Entry ee; cin>>ee; )
cout draw(); }; // fct3's type is function

Для fct2 я позволяю вывести тип function из инициализатора: int(string).
Очевидно, что function полезны для обратных вызовов (callback), для передачи операций в качестве аргументов, для передачи функциональных объектов и т.д. Однако это
может привести к некоторым накладным расходам во время выполнения по сравнению с прямыми вызовами. В частности, для объекта function, размер которого не вычисляется во время компиляции, может произойти выделение динамической памяти с серьезными негативными последствиями для приложений, критически важных для производительности. Готовится решение для C++23: move_only_function.
Другая проблема заключается в том, что function, будучи объектом, не участвует в перегрузке. Если вам нужно перегрузить функциональные объекты (включая лямбда-выражения), рассмотрите вариант overloaded (§15.4.1).

16.4 Функция типа
Функция типа - это функция, которая вычисляется во время компиляции с заданным
типом в качестве аргумента или возвращающая тип. Стандартная библиотека предоставляет множество функций ввода, помогающих разработчикам библиотек (и программистам в целом) писать код, который использует преимущества языка, стандартной библиотеки и кода в целом.
Для числовых типов numeric_limits из содержит разнообразную полезную информацию (§17.7). Например:
constexpr float min = numeric_limits::min();

// smallest positive float

Аналогично, размеры объектов могут быть определены с помощью встроенного
оператора sizeof (§1.4). Например:
constexpr int szi = sizeof(int);

// the number of bytes in an int

В стандартная библиотека предоставляет множество функций для запроса свойств типов. Например:
bool b = is_arithmetic_v;
// true if X is one of the (built-in) arithmetic
types
using Res = invoke_result_t;
// Res is int if f is a function that
// returns an int
decltype(f) - это вызов встроенной функции типа decltype(f), возвращающей объявлен-

ный тип своего аргумента; здесь f.
224

Некоторые функции типа создают новые типы на основе входных данных. Например:
typename
using Store = conditional_t(sizeof(T) < max, On_stack, On_heap);

Если первый (логический) аргумент conditional_t равен true, результатом будет первая
альтернатива; в противном случае - вторая. Предполагая, что On_stack и On_heap предлагают одинаковые функции доступа к T, они могут распределять свои T так, как указывают их имена. Таким образом, пользователи Store могут быть настроены в соответствии с размером объектов X. Настройка производительности, обеспечиваемая таким
выбором распределения, может быть очень значительной. Это простой пример того,
как мы можем создавать наши собственные функции типа, либо на основе стандартных, либо с помощью концептов.
Концепты - это функции типа. Когда они используются в выражениях, они являются
специфическими предикатами типа. Например:
template
auto call(F f, Args... a, Allocator alloc)
{
if constexpr (invocable) // needs an allocator?
return f(f,alloc,a...);
else
return f(f,a...);
}

Во многих случаях концепты являются лучшими функциями типа, но большая часть
стандартной библиотеки была написана до появления концептов и должна поддерживать кодовые базы, написанные до появления концептов.
Условные обозначения сбивают с толку. Стандартная библиотека использует _v для
функций типа, возвращающих значения, и _t для функций типа, возвращающие типы,
это пережиток слабо типизированных времен C и доконцептного C++. Ни одна функция
типа стандартной библиотеки не возвращает одновременно тип и значение, поэтому
эти суффиксы являются избыточными. С концептами, как в стандартной библиотеке,
так и в других местах, суффикс не требуется и не используется.
Функции типов являются частью механизмов C++ для вычислений во время компиляции, которые обеспечивают более жесткую проверку типов и более высокую производительность, чем это было бы возможно без них. Использование функций и концепцтов типов (глава 8, §14.5) часто называют метапрограммированием или (когда речь
идет о шаблонах) шаблонным метапрограммированием.

16.4.1 Предикаты типа
В стандартная библиотека предлагает десятки простых функций типа,
называемых предикатами типа, которые отвечают на фундаментальные вопросы о типах. Вот небольшая подборка:
Некоторые предикаты типа
T, A, и U - типы; все предикаты возвращают bool
is_void_v
is_integral_v
is_floating_point_v
is_class v

Является ли T типом void?
Является ли T целочисленным типом?
Является ли T типом число с плавающей точкой?
Является ли T классом (и не union)?
225

is_function_v

is_arithmetic_v
is_scalar_v

is_constructible_v
is_default_constructible_v
is_copy_constructi-

Может ли T быть создан без обязательных аргументов (конструктором по умолчанию)?
Может ли T быть создан от другого T?

ble_v
is_move_constructi-

Может ли T быть копирован или перемещён в другой T?

ble_v
is_assignable_v
is_trivially_copyable_v
is_same_v
is_base_of_v
is_convertible_v
is_iterator_v
is_invocable_v
has_virtual_destruc-

Может ли U быть присвоенным в T?
Может ли U быть присвоенным в T без пользовательских операций
копирования?
Является ли T того же типа как U?
Является ли U производным от T или оба T и U одного типа?
Может ли T быть неявно преобразованным в U?
Является ли T типом итератора?
Может ли T быть вызван со списком аргументов A...?
Имеет ли T виртуальный деструктор?

tor_v

Одно из традиционных применений этих предикатов заключается в ограничении
аргументов шаблона. Например:
template
class complex {
Scalar re, im;
public:
static_assert(is_arithmetic_v, "Sorry, I support only complex of arithmetic types");
// ...
};

Однако это – как и другие традиционные способы использования – проще и элегантнее сделать с помощью концептов:
template
class complex {
Scalar re, im;
public:
// ...
};

Во многих случаях предикаты типа, такие как is_arithmetic, скрываются в определении
концептов для большей простоты использования. Например:
template
concept Arithmetic = is_arithmetic_v;

Как ни странно, концепта std::arithmetic не существует.

226

Часто мы можем определить концепты, которые являются более общими, чем предикаты типа стандартной библиотеки. Многие предикаты типов стандартной библиотеки применяются только к встроенным типам. Мы можем определить концепт в терминах требуемых операций, как это предлагается в определении Number (§8.2.4):
template
concept Arithmetic = Number && Number;

Чаще всего использование предикатов типа стандартной библиотеки встречается
глубоко в реализации фундаментальных сервисов, часто для выделения вариантов оптимизации. Например, часть реализации std::copy(Iter, Iter1, Iter2) могла бы оптимизировать важный случай непрерывных последовательностей простых типов, таких как целые числа:
template
void cpy1(T* first, T* last, T* target)
{
if constexpr (is_trivially_copyable_v)
memcpy(first, target, (last - first) * sizeof(T));
else
while (first != last) *target++ = *first++;
}

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

16.4.2 Условные свойства
Рассмотрим определение “умного указателя”:
template
class Smart_pointer {
// ...
T& operator*() const;
T* operator->() const;
};

// -> should work if and only if T is a class

Оператор -> должен быть определен тогда и только тогда, когда T является типом
класса. Например, Smart_pointer должен иметь ->, но Smart_pointer не должен.
Мы не можем использовать if времени компиляции, потому что мы не находимся
внутри функции. Вместо этого мы пишем
template
class Smart_pointer {
// ...
T& operator*() const;
T* operator->() const requires is_class_v;
is a class
};

// -> is defined if and only if T

Предикат типа непосредственно выражает ограничение на operator->(). Мы также можем использовать концепты для этого. Не существует концепта стандартной библиотеки, требующего, чтобы тип был типом класса (то есть class, struct или union), но мы
могли бы определить один из них:
template
concept Class = is_class_v || is_union_v;

// unions are classes

227

template
class Smart_pointer {
// ...
T& operator*() const;
T* operator->() const requires Class;
class or a union
};

// -> is defined if and only if T is a

Часто концепт является более общим или просто более подходящим, чем прямое использование предиката типа стандартной библиотеки.

16.4.3 Генераторы типов
Многие функции типа возвращают типы, часто новые типы, которые они вычисляют. Я называю такие функции генераторами типов, чтобы отличать их от предикатов типов. Стандарт предлагает некоторые, например, такие:
Некоторые генераторы типов
R = remove_const_t

R это T с удалением константности (если таковая имеется)

R = add_const_t

R это const T

если T это ссылка U&, R это U иначе T
R = add_lvalue_reference_t если T это lvalue ссылка, R это T иначе T&
R = add_rvalue_reference_t если T это rvalue ссылка, R это T иначе T&&
если b истинно, R это T иначе R не определено
R = enable_if_t
R это T если b истинно; иначе U
R = conditional_t
если существует тип, к которому все T могут быть неявно преR = common_type_t
образованы, R является этим типом; в противном случае R не
определен
если T является перечислением, R является его базовым типом;
R = underlying_type_t
в противном случае ошибка
если T может быть вызван с аргументами A..., R это тип возвраR = invoke_result_t
щаемого значения; иначе ошибка
Функции такого типа обычно используются при реализации утилит, а не непосредственно в коде приложения. Из них enable_if, вероятно, является наиболее распространенным в коде до создания концептов. Например, условно включенный -> для умного
указателя традиционно реализуется примерно так:
R = remove_reference_t

template
class Smart_pointer {
// ...
T& operator*();
enable_if operator->(); // -> is defined if and only if T is a
class
};

Я не нахожу это особенно легким для чтения, а более сложные варианты использования гораздо хуже. Определение enable_if основывается на тонкой языковой функции,
называемой SFINAE (“Сбой замены не является ошибкой”). Посмотрите это (только),
если вам нужно.

16.4.4 Связанные типы
228

Все стандартные контейнеры (§12.8) и все контейнеры, разработанные в соответствии с их шаблоном, имеют некоторые связанные типы, такие как их типы значений и
типы итераторов. В и стандартная библиотека предоставляет имена
для этих:
Некоторе генераторы типов
range_value_t
iter_value_t
iterator_t

Тип диапазона элементов R
Тип элементов на которые указывает итератор T
Тип итаратора диапазона R

16.5 source_location
При записи сообщения трассировки или сообщения об ошибке мы часто хотим вывести данные о местоположении ошибки в исходнике частью этого сообщения. Стандартная библиотека предоставляет source_location для этого:
const source_location loc = source_location::current();

Эта функция current() возвращает значение source_location, описывающее место в исходном коде, где оно появляется. Класс source_location имеет элементы file() и function_name(),
возвращающие строки в стиле C, а элементы line() и column(), возвращающие целые числа
без знака.
Оберните это в функцию, и мы получим хороший первый фрагмент сообщения журнала:
void log(const string& mess = "", const source_location loc = source_location::current())
{
cout = x
ceil(x)
Округление до целого в меньшую сторону