Читать «Эффективный и современный С++. 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 предлагает еще и объявление псевдонима (alias declaration):

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&);