Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 58
Скотт Мейерс
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<typename T>
void processPointer(T* ptr);
В мире указателей есть два особых случая. Один из них — указатели void*
, поскольку их нельзя разыменовывать, увеличивать или уменьшать и т.д. Второй — указатели char*
, поскольку они обычно представляют указатели на C-строки, а не на отдельные символы. Эти особые случаи часто требуют особой обработки; будем считать, что в случае шаблона processPointer
эта особая обработка заключается в том, чтобы отвергнуть вызовы с такими типами (т.e. должно быть невозможно вызвать processPointer
с указателями типа void*
или char*
).
Это легко сделать. Достаточно удалить эти инстанцирования:
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
Теперь, если вызов processPointer
с указателями void*
или char*
является некорректным, вероятно, таковым же является и вызов с указателями const void*
или const char*
, так что эти инстанцирования обычно также следует удалить: