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

Скотт Мейерс

Конечно, ни одна из разновидностей не оказывается существенно проще другой, а ряд программистов тратит немало времени для того, чтобы верно записать синонимы для типов указателей на функции, так что пока что убедительных причин для предпочтения объявления псевдонима пока что нет.

Однако убедительная причина все же существует, и называется она — шаблоны. В частности, объявления псевдонимов могут быть шаблонизированы (и в этом случае они называются шаблонами псевдонимов), в то время как typedef — нет. Это дает программистам на С++11 простой механизм для выражения того, что в С++98 можно было выразить только хакерскими способами, с помощью typedef, вложенных в шаблонные struct. Рассмотрим, например, определение синонима для связанного списка, который использует пользовательский распределитель памяти MyAlloc. В случае шаблонов псевдонимов это просто, как семечки щелкать:

// MyAllocList<T> является синонимом для std::list<T,MyAlloc<T>>:

template <typename T>

 using MyAllocList = std::list<T, MyAlloc<T>>;

MyAllocList<Widget> lw; // Клиентский код

В случае typedef эти семечки приходится сначала долго растить:

// MyAllocList<T>::type - синоним для std::list<T,MyAlloc<T>>:

template <typename T>

struct MyAllocList {

 typedef std::list<T, MyAlloc<T>> type;

};

MyAllocList<Widget>::type lw; // Клиентский код

Все еще хуже. Если вы хотите использовать typedef в шаблоне для создания связанного списка, хранящего объекты типа, указанного параметром шаблона, имя, указанное в typedef, следует предварять ключевым словом typename:

template<typename T>

class Widget {                       // Widget<T> содержит

private:                             // MyAllocList<T>,

 typename MyAllocList<T>::type list; // как член-данные

};

Здесь MyAllocList<T>::type ссылается на тип, который зависит от параметра типа шаблона (T). Тем самым MyAllocList<T>::type является зависимым типом (dependent type), а одно из многих милых правил С++ требует, чтобы имена зависимых типов предварялись ключевым словом typename.

Если MyAllocList определен как шаблон псевдонима, это требование использования ключевого слова typename убирается (как и громоздкий суффикс “::type”):

template<typename T>

using MyAllocList = std::list<T, MyAlloc<T>>; // Как и ранее

template<typename T>

class Widget {

private:

 MyAllocList<T> list;                         // Ни typename,

                                              // ни ::type

};

Для вас MyAllocList<T> (т.e. использование шаблона псевдонима) может выглядеть как зависимый от параметра шаблона T, как и MyAllocList<T>::type (т.e. как и использование вложенного typedef), но вы не компилятор. Когда компилятор обрабатывает шаблон Widget и встречает использование MyAllocList<T> (т.e. использование шаблона псевдонима), он знает, что MyAllocList<T> является именем типа, поскольку MyAllocList является шаблоном псевдонима: он обязан быть именем типа. Тем самым MyAllocList<T> оказывается независимым типом, и спецификатор typename не является ни требуемым, ни разрешенным.