Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 48
Скотт Мейерс
Анализ вызова с переданным NULL
по сути такой же. Когда в функцию lockAndCall
передается NULL
, для параметра ptr
выводится целочисленный тип, и происходит ошибка, когда целочисленный тип передается функции f2
, которая ожидает аргумент типа std::unique_ptr<Widget>
.
В противоположность первым двум вызовам вызов с nullptr
никакими неприятностями не отличается. Когда функции lockAndCall
передается nullptr
, выведенным типом ptr
является std::nullptr_t
. При передаче ptr
в функцию f3
выполняется неявное преобразование std::nullptr_t
в Widget*
, поскольку std::nullptr_t
неявно преобразуется во все типы указателей.
Тот факт, что вывод типа шаблона приводит к “неверным” типам для 0
и NULL
(т.e. к их истинным типам, а не к представлению с их использованием нулевых указателей), является наиболее убедительной причиной для использования nullptr
вместо 0
или NULL
, когда вы хотите представить нулевой указатель. При применении nullptr
шаблоны не представляют собой никаких особых проблем. Вместе с тем фактом, что nullptr
не приводят к неприятностям при разрешении перегрузки, которым подвержены 0
и NULL
, все это приводит к однозначному выводу — если вам нужен нулевой указатель, используйте nullptr
, но не 0
и не NULL
.
Следует запомнить
• Предпочитайте применение nullptr
использованию 0
или NULL
.
• Избегайте перегрузок с использованием целочисленных типов и типов указателей.
3.3. Предпочитайте объявление псевдонимов применению typedef
Я уверен, что мы можем сойтись на том, что применение контейнеров STL — хорошая идея, и я надеюсь, что раздел 4.1 убедит вас, что хорошей идеей является применение std::unique_ptr
, но думаю, что ни один из вас не увлечется многократным написанием типов наподобие std::unique_ptr<std::unordered_map<std::string, std::string>>
. Одна мысль о таких типах лично у меня вызывает все симптомы синдрома запястного канала.
Избежать такой медицинской трагедии несложно, достаточно использовать typedef
:
typedef
std::unique_ptr<std::unordered_map<std::string, std::string>>
UPtrMapSS;
Но typedef
слишком уж какой-то девяносто восьмой… Конечно, он работает и в С++11, но стандарт С++11 предлагает еще и
using UPtrMapSS =
std::unique_ptr<std::unordered_map<std::string, std::string>>;
С учетом того, что typedef
и объявление псевдонима делают в точности одно и то же, разумно задаться вопросом “А есть ли какое-то техническое основание для того, чтобы предпочесть один способ другому?”
Да, есть, но перед тем как я его укажу, замечу, что многие программисты считают объявление псевдонима более простым для восприятия при работе с типами, включающими указатели на функции:
// FP является синонимом для указателя на функцию, принимающую
// int и const std::string& и ничего не возвращающую
typedef void (*FP) (int, const std::string&);
// То же самое, но как объявление псевдонима
using FP = void (*)(int, const std::string&);