Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [74]

Шрифт
Интервал

>

>};

Если ваша реакция на код “>std::forward” внутри лямбда-выражения — “Что за @#$%?!!”, то, вероятно, вы просто еще не читали раздел 6.3. Не беспокойтесь о нем. Главное для нас сейчас в этом лямбда-выражении — объявленные как >auto&& параметры. >func является универсальной ссылкой, которая может быть связана с любым вызываемым объектом, как lvalue, так и rvalue. params представляет собой нуль или несколько универсальных ссылок (т.e. набор параметров, являющихся универсальными ссылками), которые могут быть связаны с любым количеством объектов произвольных типов. В результате, благодаря универсальным ссылкам >auto, лямбда-выражение >timeFuncInvocation в состоянии записать время работы почти любого выполнения функции. (Чтобы разобраться в разнице между “любого” и “почти любого”, обратитесь к разделу 5.8.)

Имейте в виду, что весь этот раздел — основы универсальных ссылок — является лож… простите, абстракцией. Лежащая в основе истина, известная как свертывание ссылок (reference collapsing), является темой раздела 5.6. Но истина не делает абстракцию менее полезной. Понимание различий между rvalue-ссылками и универсальными ссылками поможет вам читать исходные тексты более точно (“Этот >T&& связывается только с rvalue или с чем угодно?”), а также избегать неоднозначностей при общении с коллегами (“Здесь я использовал универсальную ссылку, а не rvalue-ссылку…”). Оно также поможет вам в понимании смысла разделов 5.3 и 5.4, которые опираются на указанное различие. Так что примите эту абстракцию, погрузитесь в нее… Так же как законы Ньютона (технически не совсем корректные) обычно куда полезнее и проще общей теории относительности Эйнштейна (“истины”), так и понятие универсальных ссылок обычно предпочтительнее для работы, чем детальная информация о свертывании ссылок.

Следует запомнить

• Если параметр шаблона функции имеет тип >T&& для выводимого типа >T или если объект объявлен с использованием >auto&&, то параметр или объект является универсальной ссылкой.

• Если вид объявления типа не является в точности >type&& или если вывод типа не имеет места, то >type&& означает rvalue-ссылку.

• Универсальные ссылки соответствуют rvalue-ссылкам, если они инициализируются значениями rvalue. Они соответствуют lvalue-ссылкам, если они инициализируются значениями lvalue.

5.3. Используйте >std::move для rvalue-ссылок, а >std::forward — для универсальных ссылок

Rvalue-ссылки связываются только с объектами, являющимися кандидатами для перемещения. Если у вас есть параметр, представляющий собой rvalue-ссылку, вы знаете, что связанный с ним объект может быть перемещен:

>class Widget {

> Widget(Widget&& rhs); // rhs, определенно, ссылается на

> …                     // объект, который можно перемещать

>};

В этом случае вы захотите передавать подобные объекты другим функциям таким образом, чтобы разрешить им использовать преимущества “правосторонности”. Способ, которым это можно сделать, — привести параметры, связываемые с такими объектами, к rvalue. Как поясняется в разделе 5.1, >std::move не просто это делает, это та задача, для которой создана эта функция:

>class Widget {

>public:

> Widget(Widget&& rhs) // rhs является rvalue-ссылкой

> : name(std::move(rhs.name)),

> p(std::move(rhs.p))

> { … }

> …

>private:

> std::string name;

> std::shared_ptr p;

>};

С другой стороны, универсальная ссылка (см. раздел 5.2) может быть связана с объектом, который разрешено перемещать. Универсальные ссылки должны быть приведены к rvalue только тогда, когда они были инициализированы с помощью rvalue. В разделе 5.1 разъясняется, что именно это и делает функция >std::forward:

>class Widget f

>public:

> template

> void setName(T&& newName)           // newName является

> { name = std::forward(newName);} // универсальной ссылкой

>};

Короче говоря, rvalue-ссылки при их передаче в другие функции должны быть безусловно приведены к rvalue (с помощью >std::move), так как они всегда связываются с rvalue, а универсальные ссылки должны приводиться к rvalue при их передаче условно (с помощью >std::forward), поскольку они только иногда связываются с rvalue.

В разделе 5.1 поясняется, что можно добиться верного поведения rvalue-ссылок и с помощью >std::forward, но исходный текст при этом становится многословным, подверженным ошибкам и неидиоматичным, так что вы должны избегать применения >std::forward с rvalue-ссылкам. Еще худшей является идея применения >std::move к универсальным ссылкам, так как это может привести к неожиданному изменению значений lvalue (например, локальных переменных):

>class Widget {

>public:

> template

> void setName(T&& newName)      // Универсальная ссылка.

> { name = std::move(newName); } // Компилируется, но это

> …                              // очень плохое решение!

>private:

> std::string name;

> std::shared_ptr p;

>};


>std::string getWidgetName(); // Фабричная функция

>Widget w;

>auto n = getWidgetName();    // n - локальная переменная

>w.setName(n);                // Перемещение n в w!

>                             // Значение n теперь неизвестно

Здесь локальная переменная


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.