美文网首页程序员
《effective c++阅读笔记》条款46: 需要类型转换时

《effective c++阅读笔记》条款46: 需要类型转换时

作者: JimmyPan | 来源:发表于2020-05-06 12:55 被阅读0次

1.阅读这个条款时,首先要复习下条款24的内容

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

只有当参数位于参数列表内,这个参数才是隐式转换的参与者。
this并不会参与隐式转换
所以若想让所有的实参可以参与隐式转换,则要把成员函数变成非成员函数。

Class Rational{
    Rational(int numerator = 0, 
             int denominator = 1)
    const Rational & operator* (const Rational & lhs); //成员函数
}
Rational onehalf(1,2);
auto result =  onehalf * 2; //编译通过
 result = 2 * half ;//编译不过

他们调用的都是const Rational & operator* ((this),const Rational & lhs)这个成员函数
第二个乘法式调用时 2->不能对this 进行隐式转换,所以会出现编译错误,如果想让左右两个参数都可以进行隐式转换,必须让左右都为const Rational &,即是非成员函数的形式

2.条款46在条款24的情况下进行了拓展

在使用模版的情况下,并且需要类型转换,该如何定义接口

2.1 仿照条款24, 使用普通的非成员函数
#include <iostream>
using namespace std;

template <typename T>
class  Rational;
template <typename T>
Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs );
template <typename T>
class Rational{
    friend Rational operator* <T>(const Rational &lhs,const Rational &rhs );
public:
    Rational(const T & numerator = 0,const T &denominator = 1):numerator_(numerator),denominator_(denominator){}
    T numerator() const{ return numerator_;} ;
    T denominator() const {return denominator_;};
private:
    T numerator_;
    T denominator_;
};
template <typename T>
Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs ){
    return Rational<T>(lhs.numerator_*rhs.numerator_,lhs.denominator_*rhs.numerator_);
}

int main() {
    Rational<int> r(1, 2);
    Rational<int> l(1, 2);
    Rational<int> result = l * r; //编译通过
    result = r * 2; //编译失败
}

问题在于当调用这个 result = r * 2 时,在编译阶段在读到这个语句时会对以下这个模版函数实例化
template <typename T>
Rational<T> operator(const Rational<T> &lhs,const Rational<T> &rhs );
这个非成员模版函数,在这里首先根据参数进行模版推断
1.根据 r 也就是Rational<int> 进行推导,T为int,所以实例化出如下函数
template <typename T>
Rational<int> operator
(const Rational<int> &lhs,const Rational<int> &rhs );
然而隐式转换只发生在函数调用阶段(函数已经编译好存在了),模版推导在编译阶段,这样的推导无法匹配int。
所以发生编译错误。
所以第一种非成员函数是行不通的,介绍第二种非成员函数

#include <iostream>
using namespace std;

template <typename T>
class  Rational;
//template <typename T>
//Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs );
template <typename T>
class Rational{

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

public:
    Rational(const T & numerator = 0,const T &denominator = 1):numerator_(numerator),denominator_(denominator){}
    T numerator() const{ return numerator_;} ;
    T denominator() const {return denominator_;};
private:
    T numerator_;
    T denominator_;
};
//template <typename T>
//Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs ){
//    return Rational<T>(lhs.numerator_*rhs.numerator_,lhs.denominator_*rhs.numerator_);
//}

int main() {
    Rational<int> r(1, 2);
    Rational<int> l(1, 2);
    Rational<int> result = r * l;
    result = r * 2; //编译成功
}

为什么这种“在类内部非成员函数”,可以编译成功。
因为这个函数模版的实例化是在line 30 Rational<int> r(1, 2);
编译器在读到Rational<int>,开始生成以int为实参的Rational,这时顺便就将内部的
operator* 进行模版实例化。然后之后再调用 result = r * 2 ,在作用域内可以找到一个已经存在的operator*
并且可以进行隐式转换

总结

一般来说声明在类内部的函数都是成员函数,但成员函数因为存在this,函数其中一个参数无法进行隐式转。
如果使用一般声明定义在外部的非成员函数,确实没有this了,但是在进行模版实例化阶段是无法推导隐式转换的情况的。
所以就出现了一种带friend的成员内部函数。有了friend就没有this了,而成员内部的声明也满足了在类第一次被使用时进行模版实例化,调用时将存在一个已经存在的可调用的函数。所以就解决了这个问题
所以是不是可以理解为friend单独声明函数时的一个小用处就是去掉this?

相关文章

网友评论

    本文标题:《effective c++阅读笔记》条款46: 需要类型转换时

    本文链接:https://www.haomeiwen.com/subject/jxzbghtx.html