Прямая передача (C++)Прямая передача (англ. Perfect Forwarding) — идиоматический механизм передачи параметров-ссылок из одной функции в другую с сохранением константности (временная/обычная/константная) в шаблонах языка C++. Он был стандартизирован в редакции стандарта C++11 с помощью функциональности библиотеки STL и синтаксиса передаваемыx ссылок (англ. forwarding references), а также унифицирован для применения совместно с вариативными шаблонами[1][2]. Прямая передача используется в тех случаях, когда от функций и процедур обобщённого кода требуется оставлять неизменными фундаментальные свойства своих параметризованных аргументов, то есть[1]:
Практическое воплощение прямой передачи в стандарте языка реализовано с помощью функции ПредпосылкиОсобое поведение параметров — временных ссылокРассмотрим простейший объект с двумя конструкторами — один копирует поле из std::string, второй перемещает. class Obj {
public:
Obj(const std::string& x) : field(x) {}
Obj(std::string&& x) : field(std::move(x)) {} // std::move нужен!!
private:
std::string field;
}
Первая перегрузка конструктора — самая обычная из Си++03. А во второй std::move, и вот почему. Параметр Вопросы начинаются, когда параметров много — приходится делать 4, 8, 16… конструкторов. class Obj2 {
public:
Obj2(const std::string& x1, const std::string& x2) : field1(x1), field2(x2) {}
Obj2(const std::string& x1, std::string&& x2) : field1(x1), field2(std::move(x2)) {}
// …и ещё две перегрузки
private:
std::string field1, field2;
}
Существуют два способа не множить сущности: идиома «by-value+move» и метапрограммирование. Чтобы метапрограммирование работало, в Си++ добавлена… Склейка ссылокСклейку (свёртку, коллапсирование) ссылок (англ. reference collapsing) лучше всего объяснит такой код. using One = int&&;
using Two = One&; // тогда Two = int&
При переходе к передаваемым ссылкам выясняется не только тип переданного в функцию параметра, но также даётся оценка, является ли он rvalue или lvalue. Если переданный в функцию параметр является lvalue, то подставляемое значение тоже будет ссылкой на lvalue. При этом, отмечается, что объявление типа параметра шаблона в виде Склейка ссылок позволяет использовать такие шаблоны: class Obj {
public:
template <class T>
Obj(T&& x) : field(std::forward<T>(x)) {} // забежали вперёд и сделали правильно
private: // ниже объясним, почему без явной функции forward нельзя
std::string field;
}
Для таких временных ссылок в компиляторах добавлены специальные правила[7], из-за чего…
Следствие: невозможно автоматически узнать, временная ли ссылкаВернёмся к шаблонному конструктору Obj::Obj. Если не рассматривать посторонние типы, а только string, возможны три варианта.
С третьим вариантом всё в порядке, но простым выведением типов невозможно отличить первый вариант от второго. В первом варианте для максимальной производительности нужен std::move, во втором он опасен: конструктор перемещения опустошит строку, которая, возможно, ещё пригодится. Решение: std::forwardВернёмся к нашему шаблонному конструктору. template <class T>
Obj(T&& x) : field(std::forward<T>(x)) {}
Шаблон Идиома «by-value + move»Второй способ не множить сущности: параметр принимается по значению и передаётся дальше через class Obj {
public:
Obj(std::string x) : field(std::move(x)) {}
private:
std::string field;
}
Используется для небольших легко перемещаемых объектов, обычно в нешаблонном коде. Примечания
Источники
Ссылки
|
Portal di Ensiklopedia Dunia