templateclass Y // обобщенный оператор
shared_ptr operator=(shared_ptrY const r); // присваивания
...
};
• Используйте шаблонные функции-члены для генерации функций, принимающих все совместимые типы.
• Если вы объявляете шаблоны обобщенных конструкторов копирования или обобщенного оператора присваивания, то по-прежнему должны объявить обычный конструктор копирования и оператор присваивания.
Правило 46: Определяйте внутри шаблонов функции, не являющиеся членами, когда желательны преобразования типа
В правиле 24 объясняется, почему только к свободным функциям применяются неявные преобразования типов всех аргументов. В качестве примера была приведена функция operator* для класса Rational. Прежде чем продолжить чтение, рекомендую вам освежить этот пример в памяти, потому что сейчас мы вернемся к этой теме, рассмотрев безобидные, на первый взгляд, модификации примера из правила 24. Отличие только в том, что и класс Rational, и operator* в нем сделаны шаблонами:
template typename T
class Rational {
public:
Rational(const T numerator = 0, // см. в правиле 20 – почему
const T denominator = 1); // параметр передается по ссылке
const T numerator const; // см. в правиле 28 – почему
const T denominator const; // результат возвращается по
... // значению, а в правиле 3 –
// почему они константны
};
template typename T
const RationalT operator*(const RationalT lhs,
const RationalT rhs)
{...}
Как и в правиле 24, мы собираемся поддерживать смешанную арифметику, поэтому хотелось бы, чтобы приведенный ниже код компилировался. Мы не ожидаем подвохов, потому что аналогичный код в правиле 24 работал. Единственное отличие в том, что класс Rational и функция-член operator* теперь шаблоны:
Raionalint oneHalf(1, 2); // это пример из правила 24,
// но Rational – теперь шаблон
Ratinalint result = oneHalf * 2; // ошибка! Не компилируется
Тот факт, что этот код не компилируется, наводит на мысль, что в шаблоне Rational есть нечто, отличающее его от нешаблонной версии. И это на самом деле так. В правиле 24 компилятор знал, какую функцию мы пытаемся вызвать (operator*, принимающую два параметра типа Rational), здесь же ему об этом ничего не известно. Поэтому компилятор пытается
Пытаясь вывести T, компилятор смотрит на типы аргументов, переданных при вызове operator*. В данном случае это Rationalint (тип переменной oneHalf) и int (тип литерала 2). Каждый параметр рассматривается отдельно.
Вывод на основе типа oneHalf сделать легко. Первый параметр operator* объявлен как RationalT, а первый аргумент, переданный operator* (oneHalf), имеет тип Rationalint, поэтому T должен быть int. К сожалению, вывести тип другого параметра не так просто. Из объявления известно, что тип второго параметра operator* равен RationalT, но второй аргумент, переданный функции operator* (число 2), имеет тип int. Как компилятору определить, что есть T в данном случае? Можно ожидать, что он воспользутся не-explicit конструктором, чтобы преобразовать 2 в Rationalint и таким образом сделать вывод, что T есть int, но на деле этого не происходит. Компилятор не поступает так потому, что функции неявного преобразования типа