C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц Страница 6
- Категория: Компьютеры и Интернет / Программирование
- Автор: Яцек Галовиц
- Страниц: 121
- Добавлено: 2023-07-15 16:10:03
C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц краткое содержание
Прочтите описание перед тем, как прочитать онлайн книгу «C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц» бесплатно полную версию:С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.
C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц читать онлайн бесплатно
Как это делается
Переменные инициализируются в один прием. При использовании синтаксиса инициализатора могут возникнуть две разные ситуации.
1. Применение синтаксиса инициализатора с фигурными скобками без выведения типа auto:
// Три идентичных способа инициализировать переменную типа int:
int x1 = 1;
int x2 {1};
int x3 (1);
std::vector<int> v1 {1, 2, 3};
// Вектор, содержащий три переменные типа int: 1, 2, 3
std::vector<int> v2 = {1, 2, 3}; // Такой же вектор
std::vector<int> v3 (10, 20);
// Вектор, содержащий десять переменных типа int,
// каждая из которых имеет значение 20
2. Использование синтаксиса инициализатора с фигурными скобками с выведением типа auto:
auto v {1}; // v имеет тип int
auto w {1, 2}; // ошибка: при автоматическом выведении типа
// непосредственная инициализация разрешена
// только одиночными элементами! (нововведение)
auto x = {1}; // x имеет тип std::initializer_list<int>
auto y = {1, 2}; // y имеет тип std::initializer_list<int>
auto z = {1, 2, 3.0}; // ошибка: нельзя вывести тип элемента
Как это работает
Отдельно от механизма выведения типа auto оператор {} ведет себя предсказуемо, по крайней мере при инициализации обычных типов. При инициализации контейнеров наподобие std::vector, std::list и т.д. инициализатор с фигурными скобками будет соответствовать конструктору std::initializer_list этого класса-контейнера. При этом он не может соответствовать неагрегированным конструкторам (таковыми являются обычные конструкторы, в отличие от тех, что принимают список инициализаторов).
std::vector, например, предоставляет конкретный неагрегированный конструктор, заносящий в некоторое количество элементов одно и то же значение: std::vector<int> v (N, value). При записи std::vector<int> v {N, value} выбирается конструктор initializer_list, инициализирующий вектор с двумя элементами: N и value. Об этом следует помнить.
Есть интересное различие между оператором {} и вызовом конструктора с помощью обычных скобок (). В первом случае не выполняется неявных преобразований типа: int x (1.2); и int x = 1.2; инициализируют переменную x значением 1, округлив в нижнюю сторону число с плавающей точкой и преобразовав его к типу int. А вот выражение int x {1.2}; не скомпилируется, поскольку должно точно соответствовать типу конструктора.
Кто-то может поспорить о том, какой стиль инициализации является лучшим. Любители стиля с фигурными скобками говорят, что последние делают процесс явным, переменная инициализируется при вызове конструктора и эта строка кода ничего не инициализирует повторно. Более того, при использовании фигурных скобок {} будет выбран единственный подходящий конструктор, в то время как в момент применения обычных скобок () — ближайший похожий конструктор, а также выполнится преобразование типов.
Дополнительное правило, включенное в С++17, касается инициализации с выведением типа auto: несмотря на то что в C++11 тип переменной auto x{123}; (std::initializer_list<int> с одним элементом) будет определен корректно, скорее всего, это не тот тип, который нужен. В С++17 та же переменная будет типа int.
Основные правила:
□ в конструкции auto var_name {one_element}; переменная var_name будет иметь тот же тип, что и one_element;
□ конструкция auto var_name {element1, element2,}; недействительна и не будет скомпилирована;
□ конструкция auto var_name = {element1, element2,}; будет иметь тип std::initializer_list<T>, где T — тип всех элементов списка.
В С++17 гораздо сложнее случайно определить список инициализаторов.
Попытка скомпилировать эти примеры в разных компиляторах в режиме C++11 или C++14 покажет, что одни компиляторы автоматически выводят тип auto x {123}; как int, а другие — как std::initializer_list<int>. Подобный код может вызвать проблемы с переносимостью!
Разрешаем конструктору автоматически выводить полученный тип класса шаблона
Многие классы C++ обычно специализируются по типам, о чем легко догадаться по типам переменных, которые пользователь задействует при вызовах конструктора. Тем не менее до С++17 эти возможности не были стандартизированы. С++17 позволяет компилятору автоматически вывести типы шаблонов из вызовов конструктора.
Как это делается
Данную особенность очень удобно проиллюстрировать на примере создания экземпляров типа std::pair и std::tuple. Это можно сделать за один шаг:
std::pair my_pair (123, "abc"); // std::pair<int, const char*>
std::tuple my_tuple (123, 12.3, "abc"); // std::tuple<int, double, const char*>
Как это работает
Определим класс-пример, где автоматическое выведение типа шаблона будет выполняться на основе переданных значений:
template <typename T1, typename T2, typename T3>
class my_wrapper {
T1 t1;
T2 t2;
T3 t3;
public:
explicit my_wrapper(T1 t1_, T2 t2_, T3 t3_)
: t1{t1_}, t2{t2_}, t3{t3_}
{}
/* … */
};
О’кей, это всего лишь еще один класс шаблона. Вот как мы раньше создавали его объект (инстанцировали шаблон):
my_wrapper<int, double, const char *> wrapper {123, 1.23, "abc"};
Теперь же можно опустить специализацию шаблона:
my_wrapper wrapper {123, 1.23, "abc"};
До появления C++17 это было возможно только при реализации вспомогательной функции:
my_wrapper<T1, T2, T3> make_wrapper(T1 t1, T2 t2, T3 t3)
{
return {t1, t2, t3};
}
Используя подобные вспомогательные функции, можно было добиться такого же эффекта:
auto wrapper (make_wrapper(123, 1.23, "abc"));
STL предоставляет множество аналогичных инструментов: std::make_shared, std::make_unique, std::make_tuple и т.д. В C++17 эти функции могут считаться устаревшими. Но, конечно, они все еще будут работать для обеспечения обратной совместимости.
Дополнительная информация
Из данного примера мы узнали о неявном выведении типа шаблона. Однако в некоторых случаях на этот способ нельзя полагаться. Рассмотрим следующий класс-пример:
template <typename T>
struct sum {
T value;
template <typename ... Ts>
sum(Ts&& ... values) : value{(values + ...)} {}
};
Эта структура, sum, принимает произвольное количество параметров и суммирует их с помощью
Жалоба
Напишите нам, и мы в срочном порядке примем меры.