条款46:需要类型转换时请为模板定义非成员函数

Define non-member functions inside templates when type conversions are desired.

template<typename T>
class Rational {
public:
    Rational(const T& numerator = 0, const T& denominator = 1);
    ...
};

template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs) {
    ...
}

Rational<int> oneHalf(1, 2);
Rational<int> result = oneHalf * 2;    // 无法通过编译

这里编译器并不知道具体该调用哪个函数,因为 operator* 的第一个参数被声明为 Rational<T> ,而传递的实参类型为 Rational<int> ,所以 T 一定是 int。但是第二个实参为 int,编译器在 template 实参推导过程中从不将隐式类型转换函数纳入考虑。而解决的办法是在 template class 内的 friend 声明式中指涉某个特定函数:

template<typename T>
class Rational {
public:
    ...
    friend const Rational operator*(const Rational& lhs, const Rational& rhs);
};

template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational& rhs) { ... }

因为当对象 oneHalf 被声明为一个 Rational<int> 时,class Rational<int> 于是被具现化出来,而作为过程的一部分,friend 函数 operator* 也就被自动声明出来。

但是这种写法只能通过编译,无法进行连接,因为我们没有提供 friend const Rational operator* 的定义式。所以最优的写法是:

template<typename T> class Rational;
template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs);

template<typename T>
class Rational {
public:
    ...
friend
    const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs) {
        return doMultiply(lhs, rhs);
    }
};

template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs) {
    return Rational<T>(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}

Last updated

Was this helpful?