Читаем Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ полностью

Как объясняется в правиле 30, функции, определенные внутри класса, неявно объявляются встроенными; это касается и функций-друзей, подобных нашей operator*. Вы можете минимизировать эффект от неявного встраивания, сделав так, чтобы operator* не делала ничего, помимо вызова вспомогательной функции, определенной вне класса. В данном случае в этом нет особой необходимости, потому что функция operator* и так состоит всего из одной строки, но для более сложных функций с телом это может оказаться желательным. Поэтому стоит иметь в виду идиому «иметь друга, вызывающего вспомогательную функцию».

Тот факт, что Rational – это шаблонный класс, означает, что вспомогательная функция обычно также будет шаблоном, поэтому код в заголовочном файле, определяющем Rational, обычно выглядит примерно так:


template typename T class Ratonal; // объявление

// шаблона Rational

template typename T // объявление

const RationalT doMultiply(const RationalT lhs, // шаблона

const RationalT rhs); // вспомогательной

// функции

template typename T

class Rational {

public:

...

friend

const Rational operator*( const Rational lhs,

const Rational rhs) // друг объявляет

{ return doMultiply(lhs, rhs};} // вспомогательную

... // функцию

};


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


template typename T // определение шаблона

const RationalT doMultiply( const RationalT lhs, // вспомогательной

const RationalT rhs) // функции

{ // в заголовочном файле

return Rational(lhs.numerator * rhs.numerator, // при необходимости

lhs.denominator * rhs.denominator);

}


Конечно, будучи шаблоном, doMultiply не поддерживает умножения значений разного типа, но ей это и не нужно. Она вызывается только из operator*, который обеспечивает поддержку параметров смешанного типа! По существу, функция operator* поддерживает любые преобразования типа, необходимые для перемножения объектов класса Rational, а затем передает эти два объекта соответствующей конкретизации шаблона doMultiply, которая и выполняет собственно операцию умножения. Кооперация в действии, не так ли?

Что следует помнить

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

Правило 47: Используйте классы-характеристики для предоставления информации о типах

В основном библиотека STL содержит шаблоны контейнеров, итераторов и алгоритмов, но есть в ней и некоторые служебные шаблоны. Один из них называется advance. Шаблон advance перемещает указанный итератор на заданное расстояние:


template typename T, typename DistT // перемещает итератор iter

void advance(Iter T iter, DistT d); // на d элементов вперед

// если d 0, то перемещает iter

// назад


Концептуально advance делает то же самое, что предложение iter+=d, но таким способом advance не может быть реализован, потому что только итераторы с произвольным доступом поддерживают операцию +=. Для менее мощных итераторов advance реализуется путем повторения операции ++ или – ровно d раз.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже