Программирование >>  Разработка устойчивых систем 

1 ... 55 56 57 [ 58 ] 59 60 61 ... 196


лиотеки показан конструктор шаблонного класса complex, объявленный в виде вложенного шаблона:

tempiate<typename Т> class complex { public:

tempiate<class X> complex(const complex<X>&):

В стандартную библиотеку включены готовые специализации стандартного шаблона complex по параметру Т для типов float, double и long double. Вложенный шаблон-конструктор создает новый объект комплексного числа с другим базовым вещественным типом, как в следующем фрагменте:

complex<float> z(1.2): complex<doublе> w(z):

В этом объявлении w параметр Т шаблона complex замещается типом double, а параметр X - типом float. Вложенные шаблоны заметно упрощают реализацию подобных гибких преобразований.

Вложенный шаблон определяется внутри другого шаблона, и это обстоятельство должно быть отражено в префиксах при определении вложенного шаблона за пределами вмещающего класса. Например, если при реализации шаблона complex вложенный шаблон-конструктор определяется вне определения класса complex, это должно выглядеть так:

tempiate<typename Т> tempiate<typename Х>

complех<Т>::complex(const complex<X>& c) {/*Тело определения...*/}

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

int data[5] = {1.2.3.4.5}: vector<int> vKdata. data+5): vector<double> v2(vl.begin(). vl.endO):

Пока элементы vl остаются совместимыми по присваиванию с элементами v2 (как double и int), можно не волноваться. В шаблоне класса vector определен следующий конструктор, оформленный в виде вложенного шаблона:

template <class InputIterator> vector(InputIterator first. Inputlterator last, const Allocators = AllocatorO):

В приведенных выше объявлениях vector этот конструктор вызывается дважды. При инициализации vl на базе массива int тип Inputlterator соответствует int*. При инициализации v2 на базе vl используется экземпляр конструктора, в котором Inputlterator представляет vector<int>::iterator.

Вложенные шаблоны тоже могут быть классами (не только функциями). В следующем примере вложенный шаблон класса определяется за пределами вмещающего класса:

: C05:MemberClass.cpp Вложенный шаблон класса #include <iostream> #include <typeinfo> using namespace std:

tempiate<class T> class Outer {



Шаблоны функций

Подобно тому как шаблон класса описывает семейство классов, шаблон функции описывает семейство функций. Обе разновидности шаблонов создаются практически одинаково, но различаются по способу использования. При специализации щаблона icnacca всегда необходимо использовать угловые скобки и передавать все аргументы, для которых не определены значения по умолчанию. С другой стороны, в шаблонах функций аргументы часто можно опускать, а аргументы по умолчанию вообще запрещены. Рассмотрим типичную реализацию шаблона функции min О из заголовочного файла <algorithm>:

public:

tempiate<class R> class Inner { public: void fO:

tempiate<class T> template <class R> void Outer<T>::Inner<R>::f() {

cout Outer - typeid(T).name() endl:

cout Inner == typeid(R).name() endl;

cout Full Inner == typeid(*this).nameO endl;

int mainO { Outer<int>::Inner<bool> inner: inner.f();

} III:-

Оператор typeid (см. главу 8) получает один аргумент и возвращает объект typejnfo; функция name() этого объекта возвращает строку, представляющую тип аргумента. Например, вызов typeid(int).name() возвращает строку int (впрочем, вид возвращаемой строки зависит от платформы). Оператор typeid может получить выражение и вернуть объект typejnfo, описывающий тип этого выражения. Скажем, для int i вызов typeid(i).name() вернет нечто вроде int ,atypeid(&i).name() - что-нибудь вроде int * .

Результат выполнения приведенной выще программы выглядит примерно так:

Outer == int Inner == bool

Full Inner == Outer<int>:;Inner<bool>

Объявление переменной inner в основной программе создает специализации Inner<bool> и Outer<int>.

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



tempiate<typename Т> const Т& m1n(const Т& а. const Т& b) { return (а < b) ? а : b:

При вызове этого шаблона можно указать типы аргументов в угловых скобках, как это делается при использовании шаблонов классов: int Z m1n<1nt>(1. j):

Такой синтаксис сообщает компилятору, что он должен генерировать специализацию шаблона min, в которой параметр Т замещается типом int. По аналогии с именами классов, сгенерированным по шаблонам, можно считать, что компилятор генерирует функцию с именем min<int>().

Определение типа аргументов в шаблонах функций

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

int Z - min (i. j):

Если i и j относятся к одному типу int, компилятор понимает, что вам нужна специализация min<int>(), и генерирует ее автоматически. Типы должны совпадать, потому что шаблон изначально определялся с одним типовым аргументом, который применялся к обоим параметрам функции. К аргументам функций, тип которых определяется параметром шаблона, не применяются стандартные преобразования. Например, если вы захотите вычислить минимальное значение из int и double, следующая попытка вызова min() закончится неудачей:

int z - min (х. j): Переменная х типа double

Поскольку X и j относятся к разным типам, компилятору не удается подобрать единый тип для параметра Т в определении min(), и вызов не соответствует объявлению шаблона. Проблема решается явным преобразованием одного аргумента к типу другого аргумента или полным уточнением синтаксиса:

int Z min<doub1e>(x.j):

Вы приказываете компилятору сгенерировать версию min() для double, после чего переменная j может быть преобразована в double по стандартным правилам (потому что тогда будет существовать функция min<double>(const double&,const doubles.)).

Возникает искушение определить min() с двумя аргументами, чтобы функция могла вызываться с независимыми типами аргументов:

tempiate<typename Т. typename U> const Т& min(const T& a. const U& b) { return (a < b) ? a : b:

Нередко такое решение оправданно, но в данном случае оно порождает проблемы. Функция min() возвращает значение, и невозможно сколь-нибудь обоснованно выбрать, какой тип из двух следует вернуть (Т или U)?

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



1 ... 55 56 57 [ 58 ] 59 60 61 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика