Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ 16 ] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

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

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

прототип функции BigBlast в заголовочном файле "siMD Primitives.h"

void BigBlastCconst void* src, void* dest, size t bytes);

Разумеется, функция BigBlast предназначена для копирования только элементарных типов и простых старых структур данных. Эту функцию нельзя применять к типам, имеюшим нетривиальный конструктор копирования. Таким образом, желательно было бы реализовать новую функцию Сору так, чтобы использовать преимушества функции BigBlast с максимальной выгодой и при копировании объектов более сложных типов. При копировании элементарных типов функция сору "совершенно непонятным образом" будет вьшолняться быстрее.

Для того чтобы реализовать функцию Сору, нужно выполнить две проверки.

• Являются ли переменные init и Outit обычными указателями (в отличие от более сложных типов итераторов)?

• Можно ли копировать объекты, на которые ссылаются указатели Init и Outit, побитово?

Если на этапе компиляции мы положительно ответим на оба вопроса, можно применять функцию BigBlast. В противном случае нужно использовать обобщенный цикл for.

Решить такие проблемы можно с помощью характеристик. В этой главе, в основном, используется реализация характеристик из библиотеки Boost С-ы- (Boost).

2.10.1. Реализация характеристик указателей

В библиотеке Loki определен шаблонный класс TypeTraits, в котором собрано множество характеристик обобщенных типов. Этот класс специализирует шаблоны, содержащиеся в нем, и представляет результаты.

Реализация характеристик большинства типов основывается на полной или частичной специализации шаблонов (раздел 2.2). Например, приведенный ниже фрагмент кода определяет, является ли тип т указателем.

template <typename т> class TypeTraits {

private:

template <class u> struct RointerTraits {

enumCresult = false }; typedef NullType PointeeType;

template <class u> struct PointerTraits<u*> {

enum { result = true }; typedef и PointeeType;

public:

enum { isPointer = PointerTraits<T>::result };



typedef PointerTraits<T>::PointeeType PointeeType;

В первом определении вводится шаблонный класс PointerTraits, который как бы говорит; "Класс т - не указатель". Напомним, что ранее (в разделе 2.9) в этих ситуациях применялся класс NullType.

Во втором определении (выделенном в тексте) вводится частичная специализация шаблонного класса PointerTraits, соответствуюшая любому типу указателей. Для нулевого указателя, который не ссылается ни на один объект, специализация, выделенная в тексте фрагмента, квалифицируется как соответствие, более точное, чем обобшенный шаблон любого типа указателей. Следовательно, вступает в силу специализация для указателей, а переменная result принимает значение true. Класс Poi nteeType определяется соответствующим образом.

Теперь можно понять внутреннее устройство реализации класса std::vector::iterator - является он простым указателем или представляет собой некий сложный тип?

int main С) {

const bool

iterlsPtr = TypeTraits<vector<int>::iterator>::isPointer; cout « "vector<int>::iterator is " « iterisPtr ? "fast" : "smart" « \n;

Аналогично в классе TypeTraits реализуется константа isReference и определяется тип ReferenceType. Для ссылочного типа т класс ReferenceType представляет собой тип, на который ссылается объект класса т. Если класс т является обычным типом, то класс ReferencedType совпадает с ним.

Разпознавание указателей на члены класса (глава 5) немного отличается от описанного выше. Для этого нужна следующая специализация.

template <typename т> class TypeTraits

private:

template <class u> struct PToMTraits {

enum { result = false };

template <class u, class v> struct PToMTraits<u v::*> {

enum { result = true };

public:

enum { isMemberPointer = PToMTraits<T>::result };

2.10.2. Распознавание основных типов

Класс TypeTraits<T> реализует статическую константу isStdFundamental. По значению этой константы можно определить, является класс т стандартным основным типом или нет. К этим типам относится тип void и все числовые типы (которые.



в свою очередь, подразделяются на типы чисел с плавающей точкой и целочисленные типы). В классе TypeTraits реализуются константы, показывающие категорию, к которой может принадлежать заданный тип.

Немного забегая вперед, следует сказать, что списки типов (глава 3) позволяют легко определять, принадлежит ли тип заданному множеству типов. Пока нам нужно знать лищь то, что приведенное ниже выражение (в котором суффикс пп означает количество типов, перечисленных в списке) возвращает позицию класса т в списке, начиная отсчет от нуля, или число - 1, если заданного класса в списке нет.

TL: :lndexOf<T, TyP£L15T nn(,cnucoKтипов,разделенныхзапятыми>: :value

Например, значение выражения

TL::lndexOf<T, TYPELlST 4Csigned char, short int, int, long int)>::value

больще или равно нулю, только если класс Т является целочисленным типом со знаком. Ниже приведен фрагмент определения класса TypeTrait для основных типов.

template <typename т> class TypeTraits {

... как показано выше ... public:

typedef TYPELIST 4C

unsigned char, unsigned short int,

unsigned int, unsigned long int) Unsignedlnts;

typedef TYPELIST 4Csigned char, short int, int, long int) Signedlnts;

typedef TYPELIST 3(bool, char, wchar t) Otherints; typedef TYPELIST 3(float, double, long double) Floats; enum { isstdunsignedint =

TL::lndexOf<T, unsignedlnts>::value >= 0 }; enum { isstdsignedint =

TL::indexof<T, signedlnts>::value >= 0 }; enum { isStdlntegral = isstdunsignedint isStdSignedint

TL::lndexOf<T, Otherlnts>::value >= 0 }; enum { isStdFloat = TL::lndexOf<T, Floats>::value >= 0 } enum { isStdArith = isStdlntegral isStdFloat }; enum { isStdFundamental = isStdArith isStdFloat

Conversion<T, void>::sameType };

Использование списков и класса TL: :lndexOf дает возможность быстро получать информацию о типах, не прибегая к многократной специализации щаблонов. Если вы не можете устоять перед соблазном покопаться в деталях, можете сразу перейти к главе 3, но не забудьте вернуться обратно.

Фактическая реализация процедуры распознавания основных типов намного сложнее, что позволяет применять ее для распознавания более широкого спектра классов, разрабатываемых производителями программного обеспечения (например, int64 или long long).

2.10.3. Оптимальные типы параметров

В шаблонном коде иногда нужно ответить на следующий вопрос. Задан произвольный тип т. Какой способ передачи и получения объектов типа т в качестве аргу-



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ 16 ] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105