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

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

>bool isLucky(int number);

То, что С++ является наследником С, означает, что почти любой тип, который можно рассматривать как отчасти целочисленный, будет неявно преобразовываться в >int, но некоторые компилируемые вызовы могут не иметь смысла:

>if (isLucky('a')) …  // Является ли 'a' счастливым числом?

>if (isLucky(true)) … // Является ли true счастливым числом?

>if (isLucky(3.5)) …  // Следует ли выполнить усечение до 3

>                     // перед проверкой на "счастливость"?

Если счастливые числа действительно должны быть только целыми числами, хотелось бы предотвратить такие вызовы, как показано выше.

Один из способов достичь этого — создание удаленных перегрузок для типов, которые мы хотим отфильтровывать:

>bool isLucky(int number);      // Исходная функция

>bool isLucky(char) = delete;   // Отвергаем символы

>bool isLucky(bool) = delete;   // Отвергаем булевы значения

>bool isLucky(double) = delete; // Отвергаем double и float

(Комментарий у перегрузки с >double, который гласит, что отвергаются как >double, так и >float, может вас удивить, но ваше удивление исчезнет, как только вы вспомните, что при выборе между преобразованием >float в >int и >float в >double С++ предпочитает преобразование в >double. Вызов >isLucky со значением типа >float приведет к вызову перегрузки с типом >double, а не >int. Вернее, постарается привести. Тот факт, что данная перегрузка является удаленной, не позволит скомпилировать такой вызов.)

Хотя удаленные функции использовать нельзя, они являются частью вашей программы. Как таковые они принимаются во внимание при разрешении перегрузки. Вот почему при указанных выше объявлениях нежелательные вызовы >isLucky будут отклонены:

>if (isLucky('a')) …  // Ошибка! Вызов удаленной функции

>if (isLucky(true)) … // Ошибка!

>if (isLucky(3.5f)) … // Ошибка!

Еще один трюк, который могут выполнять удаленные функции (а функции-члены, объявленные как >private — нет), заключается в предотвращении использования инстанцирований шаблонов, которые должны быть запрещены. Предположим, например, что нам нужен шаблон, который работает со встроенными указателями (несмотря на совет из главы 4, “Интеллектуальные указатели”, предпочитать интеллектуальные указатели встроенным):

>template

>void processPointer(T* ptr);

В мире указателей есть два особых случая. Один из них — указатели >void*, поскольку их нельзя разыменовывать, увеличивать или уменьшать и т.д. Второй — указатели >char*, поскольку они обычно представляют указатели на C-строки, а не на отдельные символы. Эти особые случаи часто требуют особой обработки; будем считать, что в случае шаблона >processPointer эта особая обработка заключается в том, чтобы отвергнуть вызовы с такими типами (т.e. должно быть невозможно вызвать >processPointer с указателями типа >void* или >char*).

Это легко сделать. Достаточно удалить эти инстанцирования:

>template<>

>void processPointer(void*) = delete;


>template<>

>void processPointer(char*) = delete;

Теперь, если вызов >processPointer с указателями >void* или >char* является некорректным, вероятно, таковым же является и вызов с указателями >const void* или >const char*, так что эти инстанцирования обычно также следует удалить:

>template<>

>void processPointer(const void*) = delete;


>template<>

>void processPointer(const char*) = delete;

И если вы действительно хотите быть последовательными, то вы также удалите перегрузки >const volatile void* и >const volatile char*, а затем приступите к работе над перегрузками для указателей на другие стандартные типы символов: >wchar_t, >char16_t и >char32_t.

Интересно, что если у вас есть шаблон функции внутри класса и вы попытаетесь отключить некоторые инстанцирования, объявляя их >private (в духе классического соглашения С++98), то у вас ничего не получится, потому что невозможно дать специализации шаблона функции-члена другой уровень доступа, отличный от доступа в главном шаблоне. Например, если >processPointer представляет собой шаблон функции-члена в >Widget и вы хотите отключить вызовы для указателей >void*, то вот как будет выглядеть (не компилируемый) подход С++98:

>class Widget {

>public:

> …

> template

> void processPointer(T* ptr)

> { … }

>private:

> template<> // Ошибка!

> void processPointer(void*);

>};

Проблема заключается в том, что специализации шаблона должны быть написаны в области видимости пространства имен, а не области видимости класса. Эта проблема не возникает для удаленных функций, поскольку они не нуждаются в другом уровне доступа. Они могут быть удалены за пределами класса (а следовательно, в области видимости пространства имен):

>class Widget {

>public:

> …

> template

> void processPointer(T* ptr) { … }


>};


>template<> // Все еще public, но удаленная

>void Widget::processPointer(void*) = delete;

Истина заключается в том, что практика С++98 объявления функций >private без их определения в действительности была попыткой добиться того, для чего на самом деле созданы удаленные функции С++11. Будучи всего лишь имитацией, подход С++98 работает не так хорошо, как вещь, которую он имитирует. Он не работает вне классов, не всегда работает внутри классов, а когда работает, то может не работать до компоновки. Так что лучше придерживаться удаленных функций.


Еще от автора Скотт Мейерс
Эффективное использование 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 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.