Effecticve学习笔记_条款46:需要类型转换时请为模板定义非成员函数

原创 2016年08月31日 11:52:34

  条款24已经讨论过为什么惟有non-member函数才有能力“在所有实参身上实施隐式类型转换”,这里还以Rational class的operator*函数为例。
  这里将Rational和operator*模板化了:

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> opearator* (const Rational<T>& lhs, const Rational<T>& rhs) 
{......}

  下面的例子如同条款24一样,唯一不同的是Rational和operator*如今都变成了templates:

Rational<int> oneHalf(1,2); //这个例子来自条款24
Rational<int> result = oneHalf * 2; // 错误,无法通过编译

  上述过程无法通过编译,因为编译器这里不知道我们想要调用哪个函数。取而代之的是,它们试图想出什么函数被名为operator* 的template具现化(产生)出来。它们知道它们应该可以具现化某个“名为operator*并接受两个Rational< T >参数”的函数,但为完成这一具现化行动,必须先算出T是什么。
  以oneHalf进行推导,过程并不困难。operator* 的第一参数被声明为Rational< T >,而传递给operator* 的第一实参(oneHalf)的类型是Rational< int >,所以T一定是int。其他参数的推导则没有这么顺利。operator* 的第二参数被声明为Rational< T >,但传递给operator* 的第二实参(2)类型是int。由于template实参推导过程中从不将隐式类型转换函数纳入考虑,所以这里编译器并不会将2转换为Rational< int >,进而将T推导为int。一句话,隐式转换+推导T不能被同时被编译器接受。
  解决问题的思路便接着产生,编译器既然不能同时接受这两个过程,就让它们事先满足好一个条件,再由编译器执行另一个过程好了。
  如果把这个operator*放在template class里面,也就是先在生成模板类的那一步就定下T,这样编译器只要执行隐式转换这一步就可以了。
  因此我们可以这样来改:
  

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

template <class T>
const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs)
{
    // 这里友元函数的声明并不是用来访问类的私有成员的,而是用来进行事先类型推导的
    return Rational<T>(lhs.numerator() * rhs.numerator(), 
        lhs.denominator() * rhs.denominator());
}

  注意中间我们添加了一个友元函数。果然编译通过了,但链接时又报错了,原因是链接器找不到operator*的定义,这里又要说模板类中的一个特殊情况了,它不同与普通的类,模板类的友元函数只能在类中实现,所以要把函数体部分移至到类内,像下面这样:

template <class T>
class Rational
{
    …
    friend Rational operator* (const Rational& lhs, const Rational& rhs)
    {
        return Rational(lhs.numerator() * rhs.numerator(), 
        lhs.denominator() * rhs.denominator());
     }
    …
}

  这下编译和链接都没有问题了。这里还要说一下,就是移至类内后,T的标识符可以不写了,但如果非要写成下面这样,自然也是OK的。

friend Rational operator* (const Rational& lhs, const Rational& rhs)
{
    return Rational(lhs.numerator() * rhs.numerator(), 
    lhs.denominator() * rhs.denominator());
}

  operator* 里面只有一句话,但如果friend函数里面的东西太多了,可以定义一个辅助方法,比如DoMultiply(),这个DoMultiply可以放在类外去实现,DoMultiply本身不支持混合乘法(2 * SomeRational或者SomeRational * 2),但由于在operator*里面已经进行了隐式类型转换,所以到DoMultiply这一级是没有问题的。
  
  总结:当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

《Effective C++》读书笔记之item46:需要类型转换时请为模板定义非成员函数

1.在类外定义的模板函数,在实参具现化时不进行隐式类型转换:可以在函数调用过程中进行这样的转换,但是在能够调用一个函数之前,编译器必须知道那个函数存在,而为了知道它,必须先为相关的函数模板具现化参数类...

Effecticve学习笔记_条款45:运用成员函数模板接收所有兼容类型

假设有一个基类和一个派生类像下面这样:class Base {}; class Derived : public Base {};  由于类间的上行转换时安全的,我们可以得到如下的正确结果:Deriv...
  • zjwson
  • zjwson
  • 2016年08月26日 11:44
  • 248

Item 46:需要类型转换时,应当在类模板中定义非成员函数

Item 46: Define non-member functions inside templates when type conversions are desired. Item 2...
  • yangjvn
  • yangjvn
  • 2016年02月19日 11:52
  • 893

Effective C++第七章-模板和泛型编程之需要类型转换时请为模板定义非成员函数

需要类型转换时请为模板定义非成员函数在条款24中,class Rational { public: const int numerator() const {return n;} co...
  • mlyjqx
  • mlyjqx
  • 2017年07月19日 09:14
  • 132

C++箴言:类型转换时定义非成员函数

提要:《C++箴言:声明为非成员函数的时机》阐述了为什么只有non-memberfunctions(非成员     《C++箴言:声明为非成员函数的时机》阐述了为什么只有 non-membe...
  • icycode
  • icycode
  • 2011年09月08日 01:02
  • 717

C++学习笔记之——回顾const对象、const成员函数、mutable类型

原文地址:http://blog.csdn.net/ab198604/article/details/18980701 先来总结一下const的一些普遍用法: 1 可以用来定义常量...

C++数据的封装和类体内定义成员函数的学习笔记

在Java中封装数据是面向对象三大特性中的一个种,在C++中数据的封装和Java中思想一样。就是讲抽象得到的数据和行为相结合,形成一个有机的整体,也就是将数据和操作数据的函数代码进行有机的结合,形成类...
  • qhs1573
  • qhs1573
  • 2013年10月07日 23:17
  • 2117

C++学习笔记(5)----类的const和非const成员函数的重载

类的const和非const成员函数的重载 我们从一个例子说起,来看上一篇文章中的String类, 我们为它提供一个下标操作符([ ])以读写指定位置的字符(char)。 只要了解过C++...

struts2学习笔记3数据类型转换

  • 2010年04月07日 20:49
  • 3.26MB
  • 下载

条款24::若所有参数皆需类型转换,请为此采用 non-member 函数

条款24::若所有参数皆需类型转换,请为此采用 non-member 函数
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effecticve学习笔记_条款46:需要类型转换时请为模板定义非成员函数
举报原因:
原因补充:

(最多只允许输入30个字)