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

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

. В этом случае код не компилируется.

• Компиляторы выводят “неверный” тип одного или нескольких параметров >fwd. Здесь “неверный” может означать как то, что инстанцирование >fwd не компилируется с выведенными типами, так и то, что вызов >f с использованием выведенных типов >fwd ведет себя не так, как непосредственный вызов >f с аргументами, переданными в >fwd. Одним источником такого отклонения в поведении могла бы быть ситуация, когда у >f имеется перегрузка, и из-за “некорректного” вывода типов эта перегрузка >f, вызываемая в >fwd, отличалась бы от перегруженной функции >f, используемой при непосредственном вызове.

В приведенном выше вызове “>fwd({1,2,3})” проблема заключается в том, что передача инициализатора в фигурных скобках параметру шаблона функции, не объявленному как >std::initializer_list, заставляет его быть, как предписывает стандарт, “не выводимым контекстом”. На простом человеческом языке это означает, что компиляторам запрещено выводить тип для выражения >{1,2,3} в вызове >fwd, поскольку параметр >fwd не объявлен как >std::initializer_list. Не имея возможности вывести тип параметра >fwd, компиляторы, понятно, вынуждены отклонять такой вызов.

Интересно, что в разделе 1.2 поясняется, что вывод типа для переменных >auto, инициализированных с помощью инициализатора в фигурных скобках, успешен. Такие переменные считаются объектами типа >std::initializer_list, и это обеспечивает простой обходной путь для случаев, когда тип передающей функции должен быть выведен как >std::initializer_list: объявить локальную переменную как auto, а затем передать ее в передающую функцию:

>auto il = { 1, 2, 3 }; // Тип il выводится как

>                       // std::initializer_list

>fwd(il);               // OK, прямая передача il в f

>0 и >NULL в качестве нулевых указателей

В разделе 3.2 поясняется, что, когда вы пытаетесь передать в шаблон >0 или >NULL в качестве нулевого указателя, вывод типа для переданного аргумента дает вместо типа указателя целочисленный тип (обычно >int). В результате ни >0, ни >NULL не может быть передано с помощью прямой передачи как нулевой указатель. Решение проблемы простое: передавать >nullptr вместо >0 и >NULL. Детальную информацию вы можете найти в разделе 3.2.

Целочисленные члены-данные >static const и >constexpr без определений

В качестве общего правила не требуется определять в классах целочисленные члены-данные >static const и >constexpr; одних объявлений вполне достаточно. Дело в том, что компиляторы выполняют распространение >const для значений таких членов, тем самым устраняя необходимость выделять для них память. Например, рассмотрим такой код:

>class Widget {

>public:

> // Объявление MinVals:

> static constexpr std::size_t MinVals = 28;


>};


>… // Определения MinVals нет


>std::vector widgetData;

>widgetData.reserve(Widget::MinVals); // Использование MinVals

Здесь мы используем >Widget::MinVals (далее — просто >MinVals) для указания начальной емкости >widgetData, даже несмотря на то, что определения >MinVals нет. Компиляторы обходят отсутствующее определение (как и должны это делать) подстановкой значения 28 во все места, где упоминается >MinVals. Тот факт, что для значения >MinVals не выделена память, проблемой не является. Если берется адрес >MinVals (например, кто-то создает указатель на >MinVals), то >MinVals требует места в памяти (чтобы указателю было на что указывать), и тогда приведенный выше код хотя и будет компилироваться, не будет компоноваться до тех пор, пока не будет предоставлено определение >MinVals.

С учетом этого представим, что >f (которой функция >fwd передает аргумент) объявлена следующим образом:

>void f(std::size_t val);

Вызов >f с >MinVals проблемы не представляет, поскольку компиляторы просто заменяют >MinVals его значением:

>f(Widget::MinVals); // OK, рассматривается как "f(28)"

Увы, все не так хорошо, если попытаться вызвать >f через >fwd:

>fwd(Widget::MinVals); // Ошибка! Не должно компоноваться

Этот код компилируется, но не должен компоноваться. Если это напоминает вам происходящее при взятии адреса >MinVals, это хорошо, потому что проблема в обоих случаях одна и та же.

Хотя нигде в исходном коде не берется адрес >MinVals, параметром >fwd является универсальная ссылка, а ссылки в коде, сгенерированном компилятором, обычно рассматриваются как указатели. В бинарном коде программы указатели и ссылки, по сути, представляют собой одно и то же. На этом уровне можно считать, что ссылки — это просто указатели, которые автоматически разыменовываются. В таком случае передача >MinVals по ссылке фактически представляет собой то же, что и передача по указателю, а раз так, то должна иметься память, на которую этот указатель указывает. Передача целочисленных членов-данных >static const и >constexpr по ссылке в общем случае требует, чтобы они были определены, и это требование может привести к неудачному применению прямой передачи там, где эквивалентный код без прямой передачи будет успешен.

Возможно, вы обратили внимание на некоторые юркие слова, употребленные мною выше. Я сказал, что код “не должен” компоноваться. Ссылки “обычно” рассматриваются как указатели. Передача целочисленных членов-данных


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