• Effective C++ 条款46 需要类型转换时请为模板定义非成员函数


    1. 条款24举出一个Rational的例子,来说明为什么只有non-member函数才有能力"在所有实参身上实施隐式类型转换".Rational的定义如下:

    class Rational{
    public:
        Rational(int numerator=0,int denominator=1);
        int numerator()const;
        int denominator()const;
    private:
        int numerator;
        int denominator;
    };
    View Code

    operator*声明为Rational的non-member函数:

    const Rational operator*(const Rational& lhs,const Rational& rhs);

    在未涉及到模板时,这么做是正确的,如果将Rational升级为类模板,可能像这样:

    template<typename T>
    class Rational{
    public:
        Rational(const T& numerator=0,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){
        ...
    }
    View Code

        那么对于以下代码:

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

        看起来"onHalf*2"似乎应该使函数模板operator*具现化并调用它,但实际上编译不通过,根本原因在于编译器"在template实参推到过程中从不将隐式转换纳入考虑":转换函数在函数调用过程中的确被使用(如果operator*是一个函数而不是函数模板的话),但在调用一个函数之前,必须要知道那个函数存在,而为了知道它,必须先为相关的function template推导出参数类型(然后才可将适当的函数具现化出来).然而template实参推导过程中并不考虑采纳"通过构造函数而发生的"隐式类型转换!

    2. 只要利用一个事实就可以改变这种现状:template class内的friend声明式可以指涉该特定函数.也就是说Rational可以声明operator*为它的一个friend函数,class template并不依赖于template实参推导,因此编译器总是能够在class Rational具现化时得知T,因此,令Rational<T> class声明适当的operator*为其friend函数可简化整个问题:

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

        现在对operator*的调用就可以通过编译了,发生的改变是:oneHalf被声明时,class Rational<int>被具现化出来,而作为过程的一部分,friend函数operator*也就被作为一个函数而非函数模板自动声明出来,因此编译器可以在调用它时使用隐式转换函数.

        此时还未结束,以上代码虽然通过了编译,但却无法链接——Rational<int> operator*(const Rational<int>&lhs,const Rational<int>&rhs)已经被声明出来,但却没有定义.使用template是行不通的,因为此当Rational被具现化时,operator*只是作为一个普通的函数声明被具现化出来,编译器不会认为它和operator*函数模板有关联而为它具现化出一个函数实体.

        解决办法就是将operator*函数本体合并至其声明式内:

    template<typename T>
    class Rational{
    public:
        friend Rational operator*(const Rational& lhs,
                                               const Rational& rhs){
            return Rational(lhs.numerator()*rhs.numerator(),
                                    lhs.denominator()*rhs.denominator());
        }
        ...
    };
    View Code

        此时对operator*的调用可编译链接并执行.

    3. 在本条款中,friend的作用不再是提升函数或类的访问权限,而是使类型转换发生在所有参数身上:为了使该函数被自动具现化,需要把它声明在类内部,而在类内部声明non-member函数的唯一办法就是令它成为一个friend.

        由于operator*需要在Rational内部定义,它被默认声明为inline,为了使这种inline声明带来的冲击最小(本例中operator*已经是一个单行函数,但更复杂的函数也许需要这样),可以使它调用一个定义域class外部的辅助函数,由该辅助函数完成实际功能:

    template<typename T>
    const Rational<T> doMultiply(const Rational& lhs,
                                                 const Rational& rhs){
        ...
    }
    template<typename T>
    class Rational{
    public:
        friend Rational operator*(const Rational& lhs,
                                               const Rational& rhs){
            doMultiply(lhs,rhs);
        ...
    };
    View Code

        许多编译器为了实行template具现化,要求把template定义式放在头文件内,因此可能需要在头文件内定义doMultiply,但doMultiply可以不为inline.

  • 相关阅读:
    Backtrace stopped: previous frame identical to this frame (corrupt stack?)
    windows 10 无法启动 windows update 服务 错误 0x80070005 拒绝访问
    error LNK2019: 无法解析的外部符号 __imp_recv,该符号在函数 evthread_notify_drain_default 中被引用
    opencv3.1.0 在控制台程序中报错:winnt.h(6464): error C2872: ACCESS_MASK: 不明确的
    使用OCCI操作Oracle数据库写入中文乱码
    fatal error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "stdafx.h""
    清空资源管理器访问过FTP的账号、密码
    Windows系统查看xxx.dll、xxx.lib文件的导出函数、依赖文件等信息的方法
    ConvertBSTRToString导致的内存泄漏
    mxnet.base.MXNetError: src/imperative/./imperative_utils.h:70: Check failed: inputs[i]->ctx().dev_mask() == ctx.dev_mask() (1 vs. 2)
  • 原文地址:https://www.cnblogs.com/reasno/p/4802040.html
Copyright © 2020-2023  润新知