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

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

прямая передача допускает передачу строкового литерала, такого как >"Nancy", непосредственно в конструктор для >std::string внутри Person, в то время как методы, не использующие прямой передачи, вынуждены создавать временный объект >std::string из строкового литерала для удовлетворения спецификации параметра конструктора >Person.

Но прямая передача имеет свои недостатки. Один из них тот, что некоторые виды аргументов не могут быть переданными прямой передачей, несмотря на то что они могут быть переданы функциям, принимающим конкретные типы. Эти сбои прямой передачи исследуются в разделе 5.8.

Второй неприятностью является запутанность сообщений об ошибках, когда клиенты передают недопустимые аргументы. Предположим, например, что клиент, создающий объект >Person, передает строковый литерал, составленный из символов >char16_t (тип С++11 для представления 16-разрядных символов) вместо >char (из которых состоит >std::string):

>Person p(u"Konrad Zuse"); // "Konrad Zuse" состоит из

>                          // символов типа const char16_t

При использовании первых трех из рассмотренных в данном разделе подходов компиляторы увидят, что доступные конструкторы могут принимать либо >int, либо >std::string, и выведут более или менее понятное сообщение об ошибке, поясняющее, что не существует преобразования из >const char16_t[12] в >int или >std::string.

Однако при подходе с использованием прямой передачи массив >const char16_t связывается с параметром конструктора без замечаний и жалоб. Оттуда он передается конструктору члена-данных типа >std::string класса >Person, и только в этот момент обнаруживается несоответствие между тем, что было передано (массив >const char16_t), и тем, что требовалось (любой тип, приемлемый для конструктора >std::string). В результате получается впечатляющее сообщение об ошибке. Так, в одном из компиляторов оно состояло более чем из 160 строк!

В этом примере универсальная ссылка передается только один раз (из конструктора >Person в конструктор >std::string), но чем более сложна система, тем больше вероятность того, что универсальная ссылка передается через несколько слоев вызовов функций до того, как достигнет точки, в которой определяется приемлемость типа аргумента (или типов). Чем большее количество раз будет передаваться универсальная ссылка, тем более непонятными и громоздкими будут выглядеть сообщения об ошибках, если что-то пойдет не так. Многие разработчики считают, что одно это является основанием для применения универсальных ссылок только там, где особенно важна производительность.

В случае класса >Person мы знаем, что параметр универсальной ссылки передающей функции должен выступать в роли инициализатора для >std::string, так что мы можем использовать >static_assert для того, чтобы убедиться в его пригодности для этой роли. Свойство типа >std::is_constructible выполняет проверку времени компиляции того, может ли объект одного типа быть построен из объекта (или множества объектов) другого типа (или множества типов), так что написать такую проверку несложно:

>class Person {

>public:

> template< // Как и ранее

>  typename T,

>  typename = std::enable_if_t<

>   !std::is_base_of>::value

>   &&

>   !std::is_integral>::value

>  >

> >

> explicit Person(T&& n)

> : name(std::forward(n)) {

>  // Проверка возможности создания std::string из T

>  static_assert(

>   std::is_constructible::value,

>   "Параметр n не может использоваться для "

>   "конструирования std::string"

>  };

>  … // Здесь идет код обычного конструктора

> }


> …  // Остальная часть класса Person (как ранее)

>};

В результате при попытке клиентского кода создать объект >Person из типа, непригодного для построения >std::string, будет выводиться указанное сообщение об ошибке. К сожалению, в этом примере >static_assert находится в теле конструктора, а передающий код, являясь частью списка инициализации членов, ему предшествует. Использовавшиеся мною компиляторы выводят ясное и понятное сообщение об ошибке от >static_assert, но только после обычных сообщений (всех этих 160 с лишним строк).

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

• Альтернативы комбинации универсальных ссылок и перегрузки включают использование различных имен, передачу параметров как lvalue-ссылок на >const, передачу параметров по значению и использование диспетчеризации дескрипторов.

• Ограничение шаблонов с помощью >std::enable_if позволяет использовать универсальные ссылки и перегрузки совместно, но управляет условиями, при которых компиляторы могут использовать перегрузки с универсальными ссылками.

• Параметры, представляющие собой универсальные ссылки, часто имеют преимущества высокой эффективности, но обычно их недостатком является сложность использования.

5.6. Свертывание ссылок

В разделе 5.1 говорилось, что, когда аргумент передается в шаблонную функцию, выведенный для параметра шаблона тип указывает, является ли аргумент lvalue или rvalue. В разделе не было упомянуто, что это происходит только тогда, когда аргумент используется для инициализации параметра, являющегося универсальной ссылкой, но тому есть уважительная причина: универсальные ссылки появились только в разделе 5.2. Вместе эти наблюдения об универсальных ссылках и кодировании lvalue/rvalue означают, что для шаблона


Еще от автора Скотт Мейерс
Эффективное использование 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 так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.