effective c++摘

来源:互联网 发布:233网校软件 编辑:程序博客网 时间:2024/06/16 06:16

item 46 通过friend实现参数的隐式转换

很自然的,一个有理数类的模板形式如下:

template <typename T>struct Rational{    Rational(const T& numerator=0,const T& denominator=1)    {    }}template <typename T>const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){}

但是它不支持

Rational<int> oneHalf(1,2);Rational<int> result = 2*oneHalf;

编译器无法从operator*调用动作中推测出使用哪个函数。因为在template实参推导过程中从不将隐式类型转换函数纳入考虑。此处不考虑“通过构造函数而发生的“隐式类型转换,解决这个问题,只要利用一个事实:

template class内的friend声明式可以指涉某个特定函数

Rational类变成了:

template <typename T>struct Rational{    Rational(const T numerator=0,const T denominator=1)    {    }    friend const Rational& operator*(const Rational& lhs, const Rational& rhs)    {    }};

这项技术的一个有趣点是,我们虽然使用friend,却与friend的传统用途毫不相干。为了让类型转换可能发生于所有实参身上,我们需要一个non-member参数;为了令这个函数被自动具现化,我们需要将它声明在class内部;而在class内部声明non-member函数的唯一办法就是:令它成为一个friend。

这里必须把operator*定义也放到Rational内,因为如果只是一个声明,编译器在实例化Rational时,会寻找针对该实例的operator*的定义,然而找不到,链接就无法通过。

解决这个问题可以通过引入一个辅助函数,最后的形式如下:

//这个前置声明 work for doMultiply的声明template <typename T>struct Rational;//不能在这里进行定义,因为我们还不知道Rational的结构template <typename T>const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs);template <typename T>struct Rational{    Rational(const T numerator=0,const T denominator=1)    {    }    friend const Rational& operator*(const Rational& lhs, const Rational& rhs)    {        return doMultiply(lhs, rhs);    }};template <typename T>const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs){    //坐真正需要做的事}

item 47 traits技术

原书讲的太透彻了

item 48 template元编程

在编译期计算阶乘的例子:

template <int n>struct Factorial{    enum {value=n*Factorial<n-1>::value};};template <>struct Factorial<0>{    enum {value=1};};int main(){    printf("%d\n",Factorial<10>::value);}

但有一些局限:
只能求在编译器就能确定的整数的阶乘;
不能判别参数为负的情况;
当嵌套层数过深时编译器会报 fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)

这个例子中并没有对模板进行具现,所以工作都放在了编译期。
把enum换成static成员也是一样的:

template <int n>struct Factorial{    static int value;};template <int n>int Factorial<n>::value=Factorial<n-1>::value*n;template <>struct Factorial<0>{    static int value;};int Factorial<0>::value=1;int main(){    printf("%d\n",Factorial<3>::value);}

其他

item 04
不同编译单元non-local static对象的初始化次序无法保证,可以通过转化为local static对象来解决,但要考虑多线程下的初始化不确定性。

item11
确保当对象自我赋值时有良好的行为,技术包括比较两个对象的地址,临时存储原对象指针,copy-and-swap。

派生类中的函数会掩盖基类中的同名函数,无关参数,函数名的掩盖是语法分析时发生的,function signature是编译器生效的。

private继承中的派生类不会像public继承那样可以向基类转换,因为public继承描述的是is-a关系,private继承不是。

0 0
原创粉丝点击