回顾条款24:唯有non-member
函数才有能力在"所有实参身上实施隐士类型转换",当遇到模板时候考虑如下代码:
template<typename T>
class Rational {
public:
Rational(const T &numerator = 0, const T &denominator = 1);
const T numerator() const;
const T denominator() const;
};
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; //错误,无法通过编译error: no match for ‘operator*’ (operand types are ‘Rational<int>’ and ‘int’)
这里编译器不知道调用哪个函数,他们试图想出什么函数名为operator *
的template
具现化出来,但是必须知道T
是什么;
为了推导T
,本例子中那些类型分别是Rational<int>
和int
;每个参数分开考虑:
以onehalf
进行推导,operator *
的第一参数被声明为Rational<T>
,而传递给operator *
的第一实参的类型为Rational<int>
所以T
一定是int
;operator *
的第二参数被声明为Rational<T>
,而传递给operator *
的第二实参的类型为int
,但是template
实参推导过程中从不将隐式类型转换函数纳入考虑;
解决办法是令Rational<T> class
声明适当的operator *
为friend函数:
template<typename T>
class Rational {
public:
Rational(const T &numerator = 0, const T &denominator = 1);
const T numerator() const;
const T denominator() const;
friend const Rational operator *(const Rational &lhs, const Rational &rhs) {
}
};
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 *
的混合式调用可以通过编译了,因为当对象oneHalf
被声明为一个Rational<int>
,class Rational<int>
于是被具现化出来,而作为过程的一部分,friend
函数operator *
也就被自动声明出来,后者身为一个函数而非函数模板,因此编译器可在调用它时使用隐式转换函数;