Симуляция частичной специализации - [2]

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

>TrueType ptr_discriminator(PointerShim);

>FalseType ptr_discriminator(…);


>// IsPointer‹T›::value == true, если T является указателем,

>// IsPointer‹T›::value == false в противном случае.

>template‹class T›

>class >IsPointer {

>private:

> static T t_;

>public:

> enum {

> value = sizeof(ptr_discriminator(t_)) == sizeof(TrueType)};

>};


>// Так как объект типа void создан быть не может,

>// случай IsPointer‹void› должен обрабатываться отдельно.

>template‹›

>class IsPointer‹void› {

>public:

> enum {value = false};

>};

ПРЕДУПРЕЖДЕНИЕ Строго говоря, необходимо предоставлять не только специализацию для void, но и для соответствующих cv-квалифицированных разновидностей: const void, volatile void, const volatile void. Эти специализации опущены для краткости изложения.

ПРИМЕЧАНИЕ Функции, подобные ptr_discriminator, иногда называют дискриминирующими.

Техника основана на том, что во время компиляции выражения sizeof(ptr_discriminator(t_)) компилятор вынужден выбрать из двух перегруженных функций ptr_discriminator наиболее подходящую. В случае, если IsPointer‹T›::t_ является указателем, будет выбрана функция ptr_discriminator(PointerShim), возвращающая значение типа TrueType, и значение IsPointer‹T›::value обращается в true, т.к. sizeof(ptr_discriminator(PointerShim)) – sizeof(TrueType); в противном случае подходящей является функция ptr_discriminator(…)и значением IsPointer‹T›::value является false, т.к. sizeof(ptr_discriminator(…)) – sizeof(FalseType), а типы TrueType и FalseType выбраны таким образом, что sizeof(TrueType)!= sizeof(FalseType).

Класс PointerShim необходим для того, чтобы классы, имеющие операцию приведения к указателю, не считались указателями. На первый взгляд может показаться, что можно «упростить» дискриминирующие функции ptr_discriminator, избавившись от промежуточного класса PointerShim:

>TrueType simple_ptr_discriminator(const volatile void*);

>FalseType simple_ptr_discriminator(…);

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

>struct C {

> operator int*() const {return 0;}

>};

Так как класс C имеет операцию приведения к указателю, функция simple_ptr_discriminator может быть вызвана с любым объектом этого класса, и, следовательно, метафункция, построенная с использованием simple_ptr_discriminator, будет ошибочно определять подобные классы как указатели.

Пример. Для пущей ясности можно рассмотреть, как работает метафункция IsPointer‹T› на примере типа int. IsPointer‹int› разворачивается компилятором примерно в следующее:

>// псевдокод

>class IsPointer‹int› {

>private:

> static int t_;

>public:

> enum {value = sizeof(ptr_discriminator(t_)) == sizeof(TrueType)};

>};

ptr_discriminator(PointerShim) для t_ не подходит, т.к. объект PointerShim может быть создан только из указателя. Следовательно, подходящей будет оставшаяся ptr_discriminator(…), которая возвращает FalseType. Значит, в данном случае выражение sizeof(ptr_discriminator(t_)) эквивалентно выражению sizeof(FalseType), значение которого по условию не равно sizeof(TrueType). Следовательно, IsPointer‹int›::value == false.

Симуляция частичной специализации по виду аргумента шаблона

Использовать полученную метафункцию IsPointer‹T› для симуляции частичной специализации по виду аргумента шаблона можно примерно следующим образом:

>// Реализация общего случая: T не является указателем.

>template‹class T›

>class C_ {

> //…

>};


>// Реализация случая, когда T является указателем.

>template‹class T›

>class C_ptr_ {

> //…

>};


>// Traits для случая, когда T является указателем

>template‹bool T_is_ptr›

>struct CTraits {

> template‹class T›

> struct Args {

>  typedef C_ptr_‹T› Base;

> };

>};


>// Traits для случая, когда T не является указателем.

>template‹›

>struct CTraits‹false› {

> template‹class T› struct Args {

>  typedef C_‹T› Base;

> };

>};


>// Класс, предназначенный для использования клиентами.

>template‹class T›

>class C: public CTraits‹IsPointer‹T›::value›::template Args‹T›::Base {

> //…

>};

Ограничения

Приведенная техника симуляции частичной специализации обладает некоторыми ограничениями по сравнению с «настоящей» частичной специализацией шаблонов классов.

Одним из наиболее заметных ограничений является то, что дискриминирующие функции, применяющиеся при создании многих метафункций, требуют объявления переменной, поэтому не работают с абстрактными классами. Например, в случае с IsPointer‹T› объявляется статическая переменная t_. Несмотря на то, что ее определение не требуется, специализация шаблона IsPointer‹T› абстрактным классом приведет к ошибке компиляции. По этой же причине приходится предоставлять специализации шаблонов метафункций для void.

Другим ограничением является то, что некоторые метафункции, построенные с использованием дискриминирующих функций, например, IsConst‹T›, IsVolatile‹T›, IsReference‹T› и т.п., некорректно работают в случае, если T имеет квалификаторы и const и volatile одновременно (например, const volatile int&). Существующая реализация метафункций IsConst‹T› и IsVolatile‹T› без «настоящей» частичной специализации сводится к использованию соответствующих дискриминирующих функций:


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


DirectX 8. Начинаем работу с DirectX Graphics

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.