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

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

. Эта операция бесплатна.

Благодаря использованию std::forward lvalue-аргументы >std::string копируются в >Widget::names, в то время как rvalue-apryмeнты >std::string перемещаются. Итоговая стоимость для аргументов >std::string такая же, как и при перегрузке: одно копирование для lvalue, одно перемещение для rvalue.

В разделе 5.3 поясняется, что если вызывающая функция передает аргумент, отличный от >std: :string, он будет передан в конструктор >std::string, и это может привести к нулевому количеству копирований и перемещений >std::string. Таким образом, функции, получающие универсальные ссылки, оказываются уникально эффективными. Однако это не влияет на выполняемый нами анализ, так что для простоты мы будем предполагать, что вызывающая функция всегда передает аргументы >std::string.

• Передача по значению. Независимо от передачи lvalue или rvalue должен быть сконструирован параметр >newName. Если передано lvalue, это стоит одно копирование, если передано rvalue — одно перемещение. В теле функции >newName безусловно перемещается в >Widget::names. Итоговая стоимость, таким образом, равна одному копированию и одному перемещению для lvalue и двум перемещениям для rvalue. По сравнению с подходом с передачей ссылки мы получаем одно лишнее перемещение как для lvalue, так и для rvalue.

Взглянем еще раз на название раздела:

Рассмотрите передачу по значению для копируемых параметров, которые легко перемещаются и всегда копируются.

Оно сформулировано таким образом не без причины. Точнее, не без четырех причин.

1. Вы должны всего лишь рассмотреть использование передачи по значению. Да, при этом требуется написать всего лишь одну функцию. Да, при этом в объектном коде генерируется только одна функция. Да, вы избегаете проблем, связанных с применением универсальных ссылок. Но стоимость этого решения выше, чем стоимость альтернативных вариантов, и, как вы увидите далее, в некоторых случаях есть стоимость, которую мы еще не рассматривали.

2. Рассмотрите передачу по значению только для копируемых параметров. Параметры, не соответствующие этому условию, должны иметь типы, являющиеся только перемещаемыми, поскольку если они не копируемые, а функция всегда делает копию, такая копия должна создаваться с помощью перемещающего конструктора[30]. Вспомним, что преимущества передачи по значению перед перегрузкой заключаются в том, что при передаче по значению достаточно написать только одну функцию. Но для только перемещаемых типов нет необходимости предоставлять перегрузку для lvalue, поскольку копирование lvalue влечет вызов копирующего конструктора, который у таких типов отсутствует. Это означает, что требуется поддержка только аргументов, являющихся rvalue, и в таком случае решение на основе “перегрузки” требует только одну перегрузку, принимающую rvalue-ссылку.

Рассмотрим класс с данными-членом >std::unique_ptr и функцию установки для него. Тип >std::unique_ptr является только перемещаемым типом, так что подход с использованием “перегрузки” состоит из единственной функции.

>class Widget {

>public:

> …

> void setPtr(std::unique_ptr&& ptr)

> { p = std::move(ptr); }

>private:

> std::unique_ptr p;

>};

Вызывающая функция может использовать ее следующим образом:

>Widget w;

>…

>w.setPtr(std::make_unique("Modern С++"));

Здесь rvalue >std::unique_ptr, возвращаемое из >std::make_unique (см. раздел 4.4), передается по ссылке в >setPtr, где оно перемещается в данные-член >p. Общая стоимость составляет одно перемещение. Если бы >setPtr принимала параметры по значению,

>class Widget {

>public:

> …

> void setPtr(std::unique_ptr ptr)

> { p = std::move(ptr); }

> …

>};

то тот же вызов создавал бы параметр >ptr перемещением, а затем >ptr был бы перемещен в данные-член >p. Общая стоимость составила бы два перемещения — в два раза больше, чем при подходе с “перегрузкой”.

3. Передачу по значению стоит рассматривать только для параметров с недорогим перемещением. Когда перемещение дешевое, стоимость дополнительного перемещения может быть приемлемой, но если это не так, то излишнее перемещение становится аналогичным излишнему копированию, а важность устранения излишнего копирования и была основной причиной появления правила С++98 о нежелательности применения передачи по значению.

4. Вы должны рассматривать передачу по значению только для параметров, которые всегда копируются. Чтобы увидеть, почему это важно, предположим, что перед копированием параметра в контейнер >names функция >addName проверяет, не слишком ли короткое (или длинное) имя передано. Если это так, запрос на добавление имени игнорируется. Реализация с передачей по значению может быть написана следующим образом:

>class Widget {

>public:

> void addName(std::string newName) {

>  if ((newName.length() >= minLen) &&

>     (newName.length() <= maxLen)) {

>   names.push_back(std::move(newName));

>  }

> }

> …

>private:

> std::vector names;

>};

Эта функция берет на себя создание и уничтожение >newName, даже если в >names ничего не добавляется. Это цена, которую подход с передачей по ссылке платить не должен.


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

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


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

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


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Платформа J2Me

Эта книга научит вас, как разрабатывать программное обеспечение для платформы J2ME компании «Sun Microsystems». Эта книга придерживается стиля учебного пособия, это не справочное руководство.Цель — дать вам твердую основу в понятиях и техниках, которая даст вам возможность решиться на самостоятельную разработку качественных приложений.


Виртуальная библиотека Delphi

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


Обработка баз данных на Visual Basic.NET

Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000.


Исчерпывающее руководство по написанию всплывающих подсказок

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


Программное обеспечение встроенных систем. Общие требования к разработке и документированию

Embedded system software. General requirements for development and documentationСтандарт подготовлен в развитие ГОСТ Р ИСО/МЭК 12207-99 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.