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

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

и >constexpr по ссылке “в общем случае” требует, чтобы они были определены. Это похоже на то, как будто я что-то знаю, но ужасно не хочу вам сказать…

Это потому, что так и есть. В соответствии со стандартом передача >MinVals по ссылке требует, чтобы этот член-данные был определен. Но не все реализации выполняют это требование. Так что в зависимости от ваших компиляторов и компоновщиков вы можете обнаружить, что в состоянии выполнить прямую передачу целочисленных членов-данных >static const и >constexpr, которые не были определены. Если это так — поздравляю, но нет причин ожидать, что такой код будет переносим. Чтобы сделать его переносимым, просто добавьте определение интересующего вас целочисленного члена-данных, объявленного как >static const или >constexpr. Для MinVals это определение имеет следующий вид:

>constexpr std::size_t Widget::MinVals; // В .cpp-файле Widget

Обратите внимание, что в определении не повторяется инициализатор (28 в случае >MinVals). Не переживайте об этом. Если вы забудете предоставить инициализатор в обоих местах, компиляторы будут жаловаться, тем самым напоминая вам о необходимости указать его только один раз.

Имена перегруженных функций и имена шаблонов

Предположим, что поведение нашей функции >f (которой мы хотим передать аргументы через шаблон >fwd) может настраиваться путем передачи ей некоторой функции, выполняющей определенную работу. В предположении, что эта функция получает и возвращает >int, функция >f может быть объявлена следующим образом:

>void f(int (*pf)(int)); // pf - функция обработки

Стоит заметить, что >f может также быть объявлена с помощью более простого синтаксиса без указателей. Такое объявление может имеет следующий вид, несмотря на то что оно означает в точности то же, что и объявление выше:

>void f(int pf(int));    // Объявление той же f, что и выше

В любом случае теперь предположим, что у нас есть перегруженная функция >processVal:

>int processVal(int value);

>int processVal(int value, int priority);

Мы можем передать >processVal функции >f,

>f(processVal); // Без проблем

но это выглядит удивительным. Функции >f в качестве аргумента требуется указатель на функцию, но >processVal не является ни указателем на функцию, ни даже функцией; это имя двух разных функций. Однако компиляторы знают, какая >processVal нужна: та, которая соответствует типу параметра функции >f. Таким образом, они могут выбрать >processVal, принимающую один >int, а затем передать адрес этой функции в функцию >f.

Все это работает благодаря тому, что объявление >f позволяет компиляторам вывести требующуюся версию функции >processVal. Однако >fwd представляет собой шаблон функции, не имеющий информации о том, какой тип ему требуется, а потому компиляторы не в состоянии определить, какая из перегрузок должна быть передана:

>fwd(processVal); // Ошибка! Какая processVal?

Само по себе имя >processVal не имеет типа. Без типа не может быть вывода типа, а без вывода типа мы получаем еще один случай неудачной прямой передачи.

Та же проблема возникает и если мы пытаемся использовать шаблон функции вместо перегруженного имени функции или в дополнение к нему. Шаблон функции представляет не единственную функцию, он представляет множество функций:

>template

>T workOnVal(T param) // Шаблон для обработки значений

>{ … }


>fwd(workOnVal);      // Ошибка! Какое инстанцирование workOnVal?

Получить функцию прямой передачи наподобие >fwd, принимающую имя перегруженной функции или имя шаблона, можно, вручную указав перегрузку (или инстанцирование), которую вы хотите передать. Например, вы можете создать указатель на функцию того же типа, что и параметр >f, инициализировать этот указатель с помощью >processVal или >workOnVal (тем самым обеспечивая выбор корректной версии >processVal или генерацию корректного инстанцирования >workOnVal) и передать его шаблону >fwd:

>using ProcessFuncType =         // Псевдоним типа (раздел 3.3)

> int (*)(int);

>ProcessFuncType processValPtr = // Определяем необходимую

> processVal;                    // сигнатуру processVal

>fwd(processValPtr);             // OK

>fwd(static_cast(workOnVal)); // Также OK

Конечно, это требует знания типа указателя на функцию, передаваемого шаблоном >fwd. Разумно предположить, что функция с прямой передачей это документирует. В конце концов, функции с прямой передачей предназначены для принятия всего что угодно, так что если нет никакой документации, говорящей, что должно быть передано, то откуда вы узнаете, что им передавать?

Битовые поля

Последний случай неудачной прямой передачи — когда в качестве аргумента функции используется битовое поле. Чтобы увидеть, что это означает на практике, рассмотрим заголовок IPv4, который может быть смоделирован следующим образом:[19]

>struct IPv4Header {

> std::uint32_t version: 4,

>               IHL: 4,

>               DSCP: 6,

>               ECN: 2,

>               totalLength: 16;

>};

Если наша многострадальная функция >f (являющаяся целевой для нашей функции прямой передачи >fwd) объявлена как принимающая параметр >std::size_t, то вызов, скажем, с полем >totalLength объекта IPv4Header компилируется без проблем:


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