Симуляция частичной специализации (Кузнецов) - страница 2

Использование

Метапрограммирование и метафункции

Прежде чем перейти к изложению дальнейшего материала, полезно ввести понятия метапрограммирования и метафункции. Если внимательнее посмотреть на то, что происходит, когда компилятор встречает пример, подобный наследованию класса Matrix от MatrixTraits‹T›::…::Base, можно заметить, что фактически это является программированием компилятора. То есть, в данном случае компилятор как бы получает инструкцию: «если тип шаблона является типом float, то считать базовым классом Matrix_float_‹›, в противном случае – Matrix_‹›. Это можно рассматривать как программирование вычислений времени компиляции. Подобные техники иногда называют метапрограммированием шаблонами или просто метапрограммированием, а шаблоны, подобные MatrixTraits, – метафункциями.

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

Одним из аспектов частичной специализации является возможность специализировать шаблон по виду аргумента, например, предоставить общую для всех указателей специализацию шаблона:

>template‹class T›

>class С {

> //…

>};


>template‹class T›

>class С‹T*› {

> //…

>};

Применительно к описанной технике, проблему можно свести к задаче создания метафункции, определяющей, является ли данный тип указателем:

>template‹class T›

>struct IsPointer {

> static const bool value =…;

>};

где IsPointer‹T›::value принимает значения true или false в зависимости от того, является ли тип T указателем.

ПРИМЕЧАНИЕ Так как некоторые компиляторы не поддерживают должным образом определение статических констант времени компиляции в теле класса, эта метафункция может быть переписана эквивалентным образом с использованием enum.

Метафункция IsPointer‹T›

Задачу построения подобной метафункции решили в 2000 году сотрудники Adobe Systems Incorporated Мэт Маркус и Джесс Джонс. Суть решения сводится к использованию выражения вызова перегруженных функций внутри sizeof():

>// Типы TrueType и FalseType могут быть определены произвольным образом,

>// главное чтобы выполнялось условие: sizeof(TrueType)!= sizeof(FalseType).

>struct TrueType {char dummy_ [1];};

>struct FalseType {char dummy_ [100];};


>// Промежуточный класс PointerShim нужен,

>// чтобы избежать ошибочной работы метафункции

>// IsPointer в случае параметризации классом, в котором определен

>// оператор преобразования к указателю.

>struct PointerShim {

> PointerShim(const volatile void*);

>};

>// Т.к. функции ptr_discriminator на самом деле не вызываются, реализации не требуется.

>TrueType ptr_discriminator(PointerShim);

>FalseType ptr_discriminator(…);