В любом классе, как в обычном, так и в шаблоне, можно объявить метод-шаблон (см. п. п. 14.5.2 в [1]). Наш шаблонный класс TSimpleArray не позволяет присваивать массивы разной длины и (или) массивы с разными типами элементов.
Между тем вполне можно было бы разрешить присвоение «меньшего» массива «большему» — как по размеру, так и по типу. Например, операцию присваивания можно разрешить для следующих массивов:
TSimpleArray<int, 10> t; TSimpleArray<int, 5> г:
t = г; // больший размер = меньший размер
TSimpleArray<double, 10> ш: TSimpleArray<int, 10> п;
m = п: // больший тип = меньший тип
TSimpleArray<double, 10> у; TSimpleArray<int, 5> х:
у = х; // больший = меньший
В первом случае массив меньшего размера г копируется в массив большего размера t. Массивы TSimpleArray — не динамические, количество элементов во время выполнения не изменяется, поэтому «лишние» элементы t можно либо заполнять нулями, либо оставлять без изменения. Остановимся на втором варианте. Во втором присваивании размеры одинаковы, а тип принимающего массива «больше». В этом случае просто выполняется преобразование типа. И третий вариант объединяет оба предыдущих.
Таким образом, нам требуется реализовать операцию присваивания, в которой аргументы правого и левого типов представляют собой переменные TSimpleArray разных типов и размеров. Это можно сделать, определив метод-шаблон. Поскольку слева и справа в операции участвуют разные типы TSimpleArray, принимающий массив, очевидно, должен быть нашим основным типом, параметры которого указаны в шаблоне класса, а правый аргумент — массив, параметры которого задаются в методе. Прототип метода тогда выглядит так:
template<typename Tl, std::size_t NI>
TSimpleArray<T,N>& operator=(const TSimpleArray<TI, NI>& rhs); Модифицированный шаблон TSimpleArray представлен в листинге 11.10.
Листинг 11.10. Метод-шаблон в шаблонном классе
#include <cstdlib> // для size_t
template<typename Т = double, std::size_t N = 10> class TSimpleArray { public:
// типы typedef T typedef T& typedef const T& typedef std::size_t
value__type; reference;
const_reference; si ze_type;
// конструкторы и присваивание template<typename Tl, std::size_t NI>
TSimpleArray<T,N>& operator=(const TSimpleArray<TI, NI>& rhs); TSimpleArray(const T &t = T()); // получение размера
size_type size() const { return static_size; }
Листинг 11.10 (продолжение)
II доступ к элементам reference operator[](const size_type& i) { rangecheck(i); return elem[i]; } const_reference operator[](const size_type& i) const { rangecheck(i); return elem[i]; } private:
static const size_type static_size = N; // размер массива // проверка индекса
void rangecheck (const size_type& i) const
{ if (i >= size()) { throw std::range_error("TSimpleArray - range!"); } } T elem[N];
};
template<typename T, std::size_t N> TSimpleArray<T,N>::TSimpleArray(const T &t = T()) { for (int i = 0; i<N; i++) elem[i] = t; } // Опредепение метода-шабпона
template<typename T, std::size_t N> // внешний шаблон (класса)
template<typename Tl, std::size_t NI> // внутренний шаблон (метода)
TSimpleArray<T,N>& // возвращаемый тип
TSimpleArray<T,N>::operator=(const TSimpleArray<TI, NI>& rhs)
{ if ((void *)this!=(void *)&rhs) // проверка самоприсваивания
{ if (N>=NI) // проверка размера
for(int i = 0; i<NI; ++i)
elem[i]=static_cast<T>(rhs[i]); // преобразование типа
}
return *this;
}
Обратите внимание, что определение метода-шаблона начинается с заголовка template шаблонного класса, а затем уже задается его собственный заголовок template. Так как нам неизвестен тип в шаблоне-методе, то приходится адреса основного типа и присваиваемого типа приводить к void*.
Заметим, что определенный нами шаблон операции присваивания не замещает оператор присваивания, генерируемый по умолчанию (см. сноску 109 на с. 209 п. 12.8 в [1]). Для присвоения массивов одного типа по-прежнему будет вызываться стандартный оператор присваивания. Это можно увидеть, выполнив в отладчике программу из листинга 11.11. Для сокращения текста шаблонный класс не указан.
Листинг 11.11. Проверка метода-шаблона
#include <iostream>
// здесь должен быть шаблонный класс
int main()
{ TSimpleArray<int, 10> t;
for(int i = 1; i<10; ++i) t[i] = i;
for(int i = 0; i<10; ++i) std::cout << t[i]<< ' ';
std::cout << std::endl;
TSimpleArray<int, 10> tt; // тип tt = типу t
tt = t; // встроенная
TSimpleArray<int. 3> p; // p.sizeO < t.sizeO
for(int i = 0; i<3; ++i) p[i] t = P;
for(int i = 0; i<10; ++i) std std::cout << std::endl; TSimpleArray<double, 12> w; w = p;
for(int i = 0; i<12; ++i) std std::cout << std::endl; w = tt:
for(int i = 0; i<12; ++i) std std::cout << std::endl; return 0:
i+30:
// метод-шаблон
cout << t[i]<<
// метод-шаблон
cout << w[i]<<
// метод-шаблон
cout << w[i]<<
}
При выполнении следующего присваивания выполняется встроенная операция, генерируемая по умолчанию:
tt =
t:
Утеплить свою квартиру можно с помощью окон - http://www.plastika-okon.ru.
В остальных случаях выполняется реализованная операция-шаблон. Аналогично шаблон конструктора копирования никогда не замещает генерируемый по умолчанию конструктор копирования (см. сноску 106 на с. 207 п. 12.8 в [1]). Методы-шаблоны не могут быть виртуальными, хотя для обычных методов шаблонного класса такого ограничения нет. Кроме того, метод-шаблон может быть определен и в обычном классе. В этом случае его определение похоже на определение шаблонной функции1, так как заголовок шаблона template только один. |