读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数
2014-04-08 23:59
661 查看
这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRational*2都可以通过编译,2不能同时进行隐式类型转换成某个Rational,再作this用)。
所以我们一般将之定义成友元函数,像下面这样:
现在我们来引入模板,可以像下面这样写,注意这里的operator*是一个独立的模板函数:
但下面main函数的两行却都不能通过编译:
原因是编译器推导T出现了困难,a * 2在编译器看来,可以由a是Rational<int>将T推导成int,但是2是什么,理想情况下编译器会尝试将它先转换成一个Rational<int>,并将T推导成int,但事实上编译器在“T推导过程中从不将隐式类型转换函数纳入考虑”。所以无论是a * 2还是2 * a都是不能通过编译的,一句话,隐式转换+推导T不能被同时被编译器接受。
解决问题的思路便接着产生,编译器既然不能同时接受这两个过程,就让它们事先满足好一个条件,再由编译器执行另一个过程好了。
如果把这个operator*放在template class里面,也就是先在生成模板类的那一步就定下T,这样编译器只要执行隐式转换这一步就可以了。
因此我们可以这样来改:
注意红色部分,我们添加了一个友元函数的声明,果然编译通过了,但链接时又报错了,原因是链接器找不到operator*的定义,这里又要说模板类中的一个特殊情况了,它不同与普通的类,模板类的友元函数只能在类中实现,所以要把函数体部分移至到类内,像下面这样:
这下编译和链接都没有问题了。这里还要说一下,就是移至类内后,T的标识符可以不写了,但如果非要写成下面这样,自然也是OK的。
operator*里面只有一句话,但如果friend函数里面的东西太多了,可以定义一个辅助方法,比如DoMultiply(),这个DoMultiply可以放在类外去实现,DoMultiply本身不支持混合乘法(2 * SomeRational或者SomeRational * 2),但由于在operator*里面已经进行了隐式类型转换,所以到DoMultiply这一级是没有问题的。
最后总结一下:
当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。
所以我们一般将之定义成友元函数,像下面这样:
class Rational { private: int numerator; int denominator; public: Rational(int n = 0, int d = 1): numerator(n), denominator(d){assert(denominator != 0);} int GetNumerator() const{return numerator;} int GetDenominator() const {return denominator;} friend const Rational operator* (const Rational& r1, const Rational& r2); }; const Rational operator* (const Rational& r1, const Rational& r2) { return Rational(r1.numerator * r2.numerator, r1.denominator * r2.denominator); }
现在我们来引入模板,可以像下面这样写,注意这里的operator*是一个独立的模板函数:
template <class T> class Rational { private: T Numerator; T Denominator; public: Rational(const T& Num = 0, const T& Den = 1) : Numerator(Num), Denominator(Den){} const T GetNumerator() const { return Numerator; } const T GetDenominator() const { return Denominator; } string ToString() const { stringstream ss; ss << Numerator << "/" << Denominator; return ss.str(); } }; template <class T> const Rational<T> operator* (const Rational<T>& a, const Rational<T>& b) { return Rational<T>(a.GetNumerator() * b.GetNumerator(), a.GetDenominator() * b.GetDenominator() ); }
但下面main函数的两行却都不能通过编译:
int main() { Rational<int> a(3, 5); Rational<int> c = a * 2; // 不能通过编译! c = 2 * a; // 不能通过编译! cout << c.ToString() << endl; }
原因是编译器推导T出现了困难,a * 2在编译器看来,可以由a是Rational<int>将T推导成int,但是2是什么,理想情况下编译器会尝试将它先转换成一个Rational<int>,并将T推导成int,但事实上编译器在“T推导过程中从不将隐式类型转换函数纳入考虑”。所以无论是a * 2还是2 * a都是不能通过编译的,一句话,隐式转换+推导T不能被同时被编译器接受。
解决问题的思路便接着产生,编译器既然不能同时接受这两个过程,就让它们事先满足好一个条件,再由编译器执行另一个过程好了。
如果把这个operator*放在template class里面,也就是先在生成模板类的那一步就定下T,这样编译器只要执行隐式转换这一步就可以了。
因此我们可以这样来改:
template <class T> class Rational { … friend Rational operator* (const Rational& a, const Rational& b); }; template <class T> const Rational<T> operator* (const Rational<T>& a, const Rational<T>& b) { // 这里友元函数的声明并不是用来访问类的私有成员的,而是用来进行事先类型推导的 return Rational<T>(a.GetNumerator() * b.GetNumerator(), a.GetDenominator() * b.GetDenominator() ); }
注意红色部分,我们添加了一个友元函数的声明,果然编译通过了,但链接时又报错了,原因是链接器找不到operator*的定义,这里又要说模板类中的一个特殊情况了,它不同与普通的类,模板类的友元函数只能在类中实现,所以要把函数体部分移至到类内,像下面这样:
template <class T> class Rational { … friend Rational operator* (const Rational& a, const Rational& b) { return Rational (a.GetNumerator() * b.GetNumerator(), a.GetDenominator() * b.GetDenominator()); } … }
这下编译和链接都没有问题了。这里还要说一下,就是移至类内后,T的标识符可以不写了,但如果非要写成下面这样,自然也是OK的。
friend Rational<T> operator* (const Rational<T>& a, const Rational<T>& b) { return Rational<T>(a.GetNumerator() * b.GetNumerator(), a.GetDenominator() * b.GetDenominator()); }
operator*里面只有一句话,但如果friend函数里面的东西太多了,可以定义一个辅助方法,比如DoMultiply(),这个DoMultiply可以放在类外去实现,DoMultiply本身不支持混合乘法(2 * SomeRational或者SomeRational * 2),但由于在operator*里面已经进行了隐式类型转换,所以到DoMultiply这一级是没有问题的。
最后总结一下:
当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。
相关文章推荐
- 读书笔记 effective c++ Item 46 如果想进行类型转换,在模板内部定义非成员函数
- Effective C++ Item 46 需要类型转换时请为模板定义非成员函数
- Effective C++第七章-模板和泛型编程之需要类型转换时请为模板定义非成员函数
- Effective C++笔记_条款46 需要类型转换时请为模板定义非成员函数
- 《Effective C++》读书笔记之item46:需要类型转换时请为模板定义非成员函数
- Effective C++ 条款 46:需要类型转换时请为模板定义非成员函数
- C++之需要类型转换时请为模板定义非成员函数(46)---《Effective C++》
- 条款46:需要类型转换时请为模板定义非成员函数
- 条款46:需要类型转换时请为模板定义非成员函数
- 条款46:需要类型转换的时候请为模板定义非成员函数
- effective C++ 条款 46:需要类型转换时请为模板定义非成员函数
- Effective C++ 条款46 需要类型转换时请为模板定义非成员函数 实现代码
- Effecticve学习笔记_条款46:需要类型转换时请为模板定义非成员函数
- [翻译] Effective C++, 3rd Edition, Item 46: 需要 type conversions(类型转换)时在 templates(模板)内定义 non-member functions(非成员函数)
- Effective C++ -----条款46:需要类型转换时请为模板定义非成员函数
- 条款47:需要类型转换时请为模板定义非成员函数
- Effective C++ 条款46 需要类型转换时请为模板定义非成员函数
- Item 46:需要类型转换时,应当在类模板中定义非成员函数
- 读书笔记_Effective_C++_条款二十四: 若所有参数皆需类型转换,请为此采用non-member函数
- Item 46:需要类型转换时,应当在类模板中定义非成员函数