5. Универсальные ссылки, прямая передача, свертка ссылок, return value optimization, проблемы перегрузки
Описание файла
PDF-файл из архива "5. Универсальные ссылки, прямая передача, свертка ссылок, return value optimization, проблемы перегрузки", который расположен в категории "". Всё это находится в предмете "проектирование больших систем с++" из 11 семестр (3 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Проектирование больших систем наC++Коноводов В. А.кафедра математической кибернетики ВМКЛекция 506.10.2017lvalue, xvalue, prvalueintf();const int& g();int&& h();// ...int x = 5;int *p = &x;f();g();h();*p;14;static_cast<int&&>(x);std::move(5);lvalue, xvalue, prvalueintf();const int& g();int&& h();// ...int x = 5;int *p = &x;f(); // prvalueg(); // lvalueh(); // xvalue*p;// lvalue14;// prvaluestatic_cast<int&&>(x); // xvaluestd::move(5); // xvaluestd::moveclass TSuperClass {public:// ...TSuperClass(const TSuperClass&);TSuperClass(TSuperClass&&);// ...};std::moveclass TSuperClass {public:// ...TSuperClass(const TSuperClass&);TSuperClass(TSuperClass&&);// ...};class TMyType {public:TMyType(const TSuperClass val) : field(std::move(val));private:TSuperClass field;}В чем тут проблема?std::forwardIstd::move выполняет безусловное приведение своегоаргумента к rvalueIstd::forward выполняет приведение только при соблюденииопределенных условий.std::forwardclass A{};void Do(const A& x) {std::cout << "call Do lvalue" << std::endl;}void Do(A&& x) {std::cout << "call Do rvalue" << std::endl;}template <typename T>void call(T&& obj) {Do(obj);}int main() {A x;call(x);call(std::move(x));}std::forwardclass A{};void Do(const A& x) {std::cout << "call Do lvalue" << std::endl;}void Do(A&& x) {std::cout << "call Do rvalue" << std::endl;}template <typename T>void call(T&& obj) {Do(obj);}int main() {A x;call(x);call(std::move(x));}call Do lvaluecall Do lvaluestd::forwardclass A{};void Do(const A& x) {std::cout << "call Do lvalue" << std::endl;}void Do(A&& x) {std::cout << "call Do rvalue" << std::endl;}template <typename T>void call(T&& obj) {Do(std::forward<T>(obj));}int main() {A x;call(x);call(std::move(x));}call Do lvaluecall Do rvalueКак работает std::forwardШаблон с универсальной ссылкойtemplate <typename T>void call(T&& obj);IЕсли в качестве аргумента передается lvalue, то Tвыводится как lvalue-ссылка.IЕсли в качестве аргумента передается rvalue, то T неявляется ссылкой.Как работает std::forwardШаблон с универсальной ссылкойtemplate <typename T>void call(T&& obj);IЕсли в качестве аргумента передается lvalue, то Tвыводится как lvalue-ссылка.IЕсли в качестве аргумента передается rvalue, то T неявляется ссылкой.int x;call(x);call(std::move(x));Как работает std::forwardШаблон с универсальной ссылкойtemplate <typename T>void call(T&& obj);IЕсли в качестве аргумента передается lvalue, то Tвыводится как lvalue-ссылка.IЕсли в качестве аргумента передается rvalue, то T неявляется ссылкой.int x;call(x); // T - int&call(std::move(x)); // T - intСвертывание ссылокСтандарт определяет следующие правила свертки ссылок,применимые для определений typedef и decltype, а такжепараметров шаблонов:IA& & становится A&IA& && становится A&IA&& & становится A&IA&& && становится A&&Свертывание ссылокtemplate <typename T>struct A {typedef T&& TRef;};// ...A<int&> x;typedef int& && TRef; → typedef int& TRef;Свертывание ссылокСвертывание ссылок применяется при:Iинстанциировании шаблонов,Iгенерации типа auto,Itypedef и using,Idecltype.Универсальные ссылки и rvalue-ссылкиclass A {public:template <typename T>void set(T&& x) {text = std::move(x);}private:std::string text;};int main() {A obj;std::string text = "123";obj.set(text); // text теперь пусто}Универсальные ссылки и rvalue-ссылкиТогда так:class A {public:void set(const std::string& x) {text = x;}void set(std::string&& x) {text = std::move(x);}private:std::string text;};Универсальные ссылки и rvalue-ссылкиВоспользуемся std::forward:class A {public:template <typename T>void set(T&& x) {text = std::forward<T>(x);}private:std::string text;};Оптимизацияtemplate <typename T>MyType f(T&& obj) { // универсальная ссылкаobj.modify();return std::forward<T>(obj);}Без std::forward — всегда копия.Оптимизацияtemplate <typename T>MyType f(T&& obj) { // универсальная ссылкаobj.modify();return std::forward<T>(obj);}Без std::forward — всегда копия.MyType f() {MyType obj;return std::move(obj);}Но это лишнее! Почему?Return value optimizationУстранение временного объекта для создание возвращаемогофункцией значения.ВместоMyType f() {MyType obj;return std::move(obj);}правильнееMyType f() {MyType obj;return obj;}Return value optimizationУстранение временного объекта для создание возвращаемогофункцией значения.ВместоMyType f() {MyType obj;return std::move(obj);}правильнееMyType f() {MyType obj;return obj;}Когда не работает RVO?Перегрузкаvoid Do(std::set<std::string>& strings,const std::string& str) {std::cout << str << std::endl;strings.emplace(str);}int main() {std::set<std::string> strings;std::string s1("text");Do(strings, s1);Do(strings, "some");Do(strings, std::string("string"));return 0;}ПерегрузкаПерепишем на универсальную ссылку:template <typename T>void Do(std::set<std::string>& strings, T&& str) {std::cout << str << std::endl;strings.emplace(std::forward<T>(str));}int main() {std::set<std::string> strings;std::string s1("text");Do(strings, s1);Do(strings, "some");Do(strings, std::string("string"));return 0;}Перегрузкаvoid Do(std::set<std::string>& strings, int x) {strings.emplace(std::to_string(x));}Перегрузкаvoid Do(std::set<std::string>& strings, int x) {strings.emplace(std::to_string(x));}И внезапно ломается код:short x = 2;Do(strings, x);Перегрузкаvoid Do(std::set<std::string>& strings, int x) {strings.emplace(std::to_string(x));}И внезапно ломается код:short x = 2;Do(strings, x);Функции с универсальными ссылками могут выполнитьинстанциирование с точным соответствием практическилюбому типу.Перегрузка: еще примерclass A {private:std::string text;public:template <typename T>explicit A(T&& str) : text(std::forward<T>(str)) {}explicit A(int x) : text(std::to_string(x)) {}};int main() {A x("123");auto copyX(x);}Перегрузка: еще примерКласс после инстанциированияclass A {private:std::string text;public:explicit A(A& str) : text(std::forward<A&>(str)) {}A(const A& rhs); // сгенерировано компиляторомexplicit A(int x) : text(std::to_string(x)) {}};Прямая передачаtemplate <typename T>void fwd(T&& x) {f(std::forward<T>(x));}Целевая функция f должна получить в точности те жеобъекты, которые переданы функции fwd.Прямая передачаtemplate <typename T>void fwd(T&& x) {f(std::forward<T>(x));}Целевая функция f должна получить в точности те жеобъекты, которые переданы функции fwd.void f(const std::vector<int>& v);f({0, 1, 0, 1});// okfwd({0, 1, 0, 1}); // errorПрямая передачаtemplate <typename T>void fwd(T&& x) {f(std::forward<T>(x));}Целевая функция f должна получить в точности те жеобъекты, которые переданы функции fwd.void f(const std::vector<int>& v);f({0, 1, 0, 1});// okfwd({0, 1, 0, 1}); // errorauto x = {0, 1, 0, 1}; // std::initializer_list<int>fwd(x);// okПеремещающие операцииПеремещающий конструктор и перемещающий операторприсваивания:Iгенерируются только при необходимости;Iвыполняют «почленное перемещение»;Iне генерируются при явном объявлении;Iне являются независимыми;Iне генерируются при явном объявлении копирующихопераций или деструктора.Перемещающие операцииЕсли все-таки нужно сгенерировать?Перемещающие операцииЕсли все-таки нужно сгенерировать?class A {public:A(A&&) = default;A& operator(A&&) = default;virtual ~A() { ...}};Некоторые выводыIIIIIIIПеремещение — новая ключевая идея С++ — обычноиспользуется для оптимизации копирования.std::move ничего не перемещает, std::forward ничего непередает.Не объявляйте объекты константными, если нужновыполнять перемещение из них.Применяйте std::move к rvalue-ссылкам, а std::forward куниверсальным ссылкам.Перегрузка для универсальных ссылок может привести кнеприятным эффектам (конструкторы с прямой передачейсоответствуют неконстантным lvalue обычно лучшекопирующих конструкторов)Большинство стандартных типов в С++11 перемещаемы,например, контейнеры STL.Некоторые типы только перемещаемы, например, объектыпотоков, std::thread, std::unique_ptr..