16.4.3. Перенаправление пакетов параметров

По новому стандарту можно использовать шаблоны с переменным количеством аргументов совместно с функцией forward() для написания функций, которые передают свои аргументы неизменными некой другой функции. Чтобы проиллюстрировать такие функции, добавим в класс StrVec (см. раздел 13.5) функцию-член emplace_back(). Такая функция-член библиотечных контейнеров является шаблоном-членом с переменным количеством аргументов (см. раздел 16.1.4), которая использует их для создания элементов непосредственно в области, управляемой контейнером. 

Версия функции emplace_back() для класса StrVec также должна быть с переменным количеством аргументов, поскольку у класса string много конструкторов, которые отличаются своими параметрами.

Поскольку желательно быть в состоянии использовать конструктор перемещения класса string, необходимо будет также сохранять всю информацию о типах аргументов, переданных функции emplace_back().

Как уже упоминалось, сохранение информации типа — двухступенчатый процесс. Во-первых, для сохранения информации типа аргументов параметры функции emplace_back() следует определить как ссылки на r-значение параметра типа шаблона (см. раздел 16.2.7):

class StrVec {

public:

 template <class... Args> void emplace_back(Args&&...);

 // остальные члены, как в разделе 13.5

};

Схема && в развертывании пакета параметров шаблона означает, что каждый параметр функции будет ссылкой на r-значение на соответствующий ей аргумент.

Во-вторых, функцию forward() следует использовать для сохранения первоначальных типов аргументов, когда функция emplace_back() передает их функции construct() (см. раздел 16.2.7):

template <class... Args>

inline

void StrVec::emplace_back(Args&&... args) {

 chk_n_alloc(); // пересоздает StrVec при необходимости

 alloc.construct(first_free++, std::forward<Args>(args)...);

}

Тело функции emplace_back() вызывает функцию chk_n_alloc() (см. раздел 13.5), чтобы гарантировать наличие достаточного места для элемента, и вызывает функцию construct(), чтобы создать элемент в позиции, на которую указывает указатель first_free.

std::forward<Args>(args)...

Развертывание в вызове функции construct() разворачивает оба пакета: параметров шаблона Args и параметров функции args. Эта схема создает элементы в формате:

std::forward<Ti>(ti)

где Ti представляет тип i-го элемента в пакете параметров шаблона, a ti представляет i-й элемент в пакете параметров функции. Например, если svec имеет тип StrVec, то при вызове

svec.emplace_back(10, 'c'); // добавит cccccccccc как новый последний

                            // элемент

схема в вызове функции construct() развернется в

std::forward<int>(10), std::forward<char>(c)

Использование функции forward() в этом вызове гарантирует, что если функция emplace_back() будет вызвана с r-значением, то функция construct() также получит r-значение. Например, в вызове

svec.emplace_back(s1 + s2); // использует конструктор перемещения

аргумент функции emplace_back() является r-значением, которое передается функции construct() как

std::forward<string>(string("the end"))

Типом результата вызова forward<string> будет strings&, поэтому функция construct() будет вызвана со ссылкой на r-значение. Функция construct(), в свою очередь, перенаправит этот аргумент конструктору перемещения класса string, чтобы создать этот элемент.

Совет. Перенаправление и шаблоны с переменным количеством аргументов

Функции с переменным количеством аргументов зачастую перенаправляют свои параметры другим функциям. Форма таких функций, как правило, подобна функции emplace_back():

// у функции fun() может быть любое количество параметров, каждый

// из которых является ссылкой r-значения на тип параметра шаблона

template<typename... Args>

void fun(Args&&... args) // развертывание Args в список ссылок

                         // на r-значения

{

 // аргумент work() развертывает как Args, так и args

 work(std::forward<Args>(args)...);

}

Здесь предполагается перенаправить все аргументы функции fun() другой функции, work(), которая, по-видимому, осуществляет реальную работу. Как и вызов функции construct() в функции emplace_back(), развертывание в вызове функции work() разворачивает и пакет параметров шаблона, и пакет параметров функции.

Поскольку параметры функции fun() являются ссылками на r-значение, функции fun() можно передать аргументы любого типа; поскольку для передачи этих аргументов используется функция std::forward(), вся информация о типах этих аргументов будет сохраняться в вызове функции work().

Упражнения раздела 16.4.3

Упражнение 16.58. Напишите функцию emplace_back() для собственного класса StrVec и для класса Vec, написанного в упражнении раздела 16.1.2.

Упражнение 16.59. С учетом того, что s имеет тип string, объясните вызов svec.emplace_back(s).

Упражнение 16.60. Объясните, как работает функция make_shared() (см. раздел 12.1.1).

Упражнение 16.61. Определите собственную версию функции make_shared().

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК