Effective C++ 46条 需要类型转换时请为模板定义非成员函数
2015-06-05 15:01
405 查看
背景:承接条款24条(请先去了解),那么,之于template实现版本呢?
我们希望以下代码编译通过:
上述的错误产生在实参推到环节上,先对oneHalf进行推导,operator*的第一个参数被声明为Rational<T>,而传递的第一个参数是Rational<int>,所以T一定是int;但是第二个参数是2,由于template实参推导不会考虑隐式类型转换函数,所以2无法转换成Rational<int>进而推导出T为int。隐式转换存在与函数调用的过程中,但是在能够调用一个函数之前,首先必须知道该函数的存在,而为了知道它,必须先为相关的函数模板推导出参数类型,然后才能将函数具现化。然而,模板实参推导过程中并不考虑采纳“通过构造函数而发生的”隐式类型转换。
应对这种问题:考虑到class Rational<T>具现化时,我们就能得知T。这样我们可在累内声明适当的operator*为其friend函数,那么可以简化整个问题:
这样2就可以调用它的隐式转换函数(Rational的non-explicit构造函数),这样就可以编译通过了,但是连接是失败的。
原因非常简单,因为声明在Rational内的operator*没有被定义出来。于是我们这样既可:
这里有一点需要澄清下,虽然我们使用friend,却与friend的传统用途“方位class的non-public成分”毫不相干。为了让类型转换可能发生于所有实参身上,我们需要一个non-member函数;为了零这个函数被自动具现化,我们需要将它声明在clss内部;而在class内部声明non-member函数的唯一办法就是:令他成为一个friend。
进一步完善,为了对inline函数的造成的冲击最小化,我们可以让其调用一个辅助函数来实现。最终版本如下:
总结:当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。
template<tyname 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;//错误无法通过编译
上述的错误产生在实参推到环节上,先对oneHalf进行推导,operator*的第一个参数被声明为Rational<T>,而传递的第一个参数是Rational<int>,所以T一定是int;但是第二个参数是2,由于template实参推导不会考虑隐式类型转换函数,所以2无法转换成Rational<int>进而推导出T为int。隐式转换存在与函数调用的过程中,但是在能够调用一个函数之前,首先必须知道该函数的存在,而为了知道它,必须先为相关的函数模板推导出参数类型,然后才能将函数具现化。然而,模板实参推导过程中并不考虑采纳“通过构造函数而发生的”隐式类型转换。
应对这种问题:考虑到class Rational<T>具现化时,我们就能得知T。这样我们可在累内声明适当的operator*为其friend函数,那么可以简化整个问题:
template<tyname T> class Rational { public: ... friend const Rational operator *(const Rational &lhs, const Rational & rhs); }; //这里是friend函数的实现 template<typename T> const Rational<T> operator*(const Rational<T> & lhs, const Rational<T> & rhs) {...}
这样2就可以调用它的隐式转换函数(Rational的non-explicit构造函数),这样就可以编译通过了,但是连接是失败的。
原因非常简单,因为声明在Rational内的operator*没有被定义出来。于是我们这样既可:
template<typename T> class Rational { public: friend const Rational operator*(const Rational & lhs, const Rational &rhs) { return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); } };
这里有一点需要澄清下,虽然我们使用friend,却与friend的传统用途“方位class的non-public成分”毫不相干。为了让类型转换可能发生于所有实参身上,我们需要一个non-member函数;为了零这个函数被自动具现化,我们需要将它声明在clss内部;而在class内部声明non-member函数的唯一办法就是:令他成为一个friend。
进一步完善,为了对inline函数的造成的冲击最小化,我们可以让其调用一个辅助函数来实现。最终版本如下:
//这是在头文件中实现的 //这个是辅助函数 template<typename T> const Rational<T> doMultiply(cosnt Rational<T> &lhs, cosnt Raional<T> &rhs); template<typename T> class Rational { public: ... friend cosnt Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs) {return doMultiply(lhs, rhs);} };
总结:当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。
相关文章推荐
- 防止资源泄漏
- vector中针对自定义类型的排序
- 一个例子演示了C++异常的推荐用法
- C++ STL 容器自定义内存分配器
- C++ 复制构造函数
- C++中虚析构函数的作用
- c语言关键字
- C++ const用法 尽可能使用const
- C/C++ 基础编程
- C++ : 窗口变化相关消息 OnSize、OnSizing和OnGetMinMaxInfo,onsizeonsizing
- C语言中main函数的參数具体解释
- 提取VS的Win32SDk用C/C++编译器 (续)——使用方法
- c语言字符串转OC字符串
- 函数重载的意义详细说明
- C++函数模板
- Leetcode Problem.35—Search Insert Position C++实现
- C++【构造函数与析构函数基础知识以及构造析构顺序】
- Cppcheck 1.54 C/C++静态代码分析工具
- c/c++string 转化其他类型
- Leetcode Problem.47—Permutations II C++实现