zoukankan      html  css  js  c++  java
  • C++学习笔记35:函数模板

    函数模板

    函数模板的目的

    • 设计通用的函数,以适应广泛的数据型式

    函数模板的定义格式

    • template<模板型式参数列表>返回值型式 函数名称(参数列表);
    • 原型:template<class T> void Swap(T &a, T&b);
    • 实现:template<class T> void Swap(T &a , T&b){...}

    函数模板的体化与特化

    • 针对特定型参数,在声明或第一次调用该函数模板时体化
    • 每次体化都形成针对特定型参数的重载函数版本
    • 文件最终只保留特定型参数的一份体化后的函数
    • 显式体化主要用于库的设计;显式特化覆盖体化的同型函体

    //函数模板

    template<class T> void f(T t) {/* */}

    //显式体化:使用显式的长整型模板参数

    template void f<long>(long n);

    //显式体化:使用d的型式推导模板参数型式

    template void f(double d);

    //显式特化:使用显式的整型参数

    template<>  void f<int> (int n);

    //显式特化:使用c的型式推导模板参数型式

    template<> void f(char c);

    template <class T> void Swap(T &a, T &b)
    {
        T a; t = a, a = b, b = t;
    }
    int main()
    {
        int m = 11, n = 7;
        char a = 'A', b = 'B';
        double c = 1.0, d = 2.0;
        //正确调用,体化Swap(int &,int &)
        Swap(m, n);
        //正确调用,体化Swap(char &,char &)
        Swap<char>(m, n);
        //正确调用,体化Swap(double &, double &)
        Swap<double>(c, d);
        return 0;
    }

    函子

    编写函数,求某个数据集的最小元,元素型式为T

    • 实现策略:使用函数指针作为回调函数参数
    • 实现策略:使用函子(function object, functor)作为回调函数参数

    函数指针实现

    //const T *a 指向数据集的基地址
    //n为元素的个数
    template<typename T>
    const T &Min(const T *a, int n, bool(*compare)(const T&, const T&))
    {
        int index = 0;
        for (int i = 1; i < n; i++)
        {
            if (comparer(a[i], a[index]))
                index = i;
        }
        return a[index];
    }

    函子

    函子的目的

      功能上:类似函数指针

      实现上:重载函数调用操作符,必要时重载小于比较操作符

    函子的优点

      函数指针不能内联,而函子可以,效率更高

      函子可以拥有任意数量的额外数据,可以保证结果和状态,提高代码的灵活性

      编译时可对函子进行型式检查

    函子实现

    //使用方法

    int a[8] = {9,2,3,4,5,6,7,8};

    int min = Min(a,8,Comparer<int>());//构造匿名函子作为函数参数

    template<typename T>class Comparer
    {
    public:
        //确保型式T已存在或重载operator<
        bool operator()(const T &a, const T &b)
        {
            return a < b;
        }
    };
    
    template<typename T,typename Comparer>
    const T &Min(const T *a, int n, Comparer comparer)
    {
        int index = 0;
        for (int i = 1; i < n; i++)
        {
            if (comparer(a[i], a[index]))
                index = i;
        }
        return a[index];
    }

    完美转发

    完美转发的意义

    • 库的设计者需要设计一个通用的函数,将接受到的参数转发给其他函数
    • 转发过程中,所有参数保持原先的语义不变

    完美转发的实现策略

    • 当需要同时提供移动语义和拷贝语义时,要求重载大量建构函数,编程量大,易出错
    • 右值引用与函数模板相互配合,可以实现完美转发,极大降低代码编写量

    例子:

    class A
    {
    public:
        A(const string &s, const string &t) :_s(s), _t(t) {}
        A(const string &s, string && t) :_s(s), _t(move(t)) {}
        A(string &&s, const string &t) :_s(move(s)), _t(t) {}
        A(string &&s, string &&t) :_s(move(s)), _t(move(t)) {}
    private:
        string _s, _t;
    };
    int main()
    {
        string s1("Hello");
        const string s2("World");
        A a1(s1, s2);
        A a3(string("Good"), s2);
        A a2(s1, string("Bingo"));
        A a4(string("Good"), string("Bingo"));
        return 0;
    }

    改进后:

    class A
    {
    public:
        //根据实际参数型式生成不同的左值或右值引用的建构函数版本
        //T1或T2可以不同型,此处相同仅为示例
        //实参推演时,使用引用折叠机制
        //当形式参数为T&&型时,当且仅当实际参数为右值或者右值引用时
        //实际参数型式才为右值引用
        //引用折叠机制与const/volatile无关,保持其参数性质不变
        //std::forward<T>(t)转发参数的右值引用T&&
        template<typename T1, typename T2> A(T1 &&s, T2 &&t)
            :_s(std::forward<T1>(s)), _t(std::forward<T2>(t)) {}
    private:
        std::string _s, _t;
    };
    怕什么真理无穷,进一寸有一寸的欢喜。---胡适
  • 相关阅读:
    可爱的中国电信 请问我们的电脑还属于我们自己吗?
    了解客户的需求,写出的代码或许才是最优秀的............
    DELPHI DATASNAP 入门操作(3)简单的主从表的简单更新【含简单事务处理】
    用数组公式获取字符在字符串中最后出现的位置
    在ehlib的DBGridEh控件中使用过滤功能(可以不用 MemTableEh 控件 适用ehlib 5.2 ehlib 5.3)
    格式化json返回的时间
    ExtJs中使用Ajax赋值给全局变量异常解决方案
    java compiler level does not match the version of the installed java project facet (转)
    收集的资料(六)ASP.NET编程中的十大技巧
    收集的资料共享出来(五)Asp.Net 权限解决办法
  • 原文地址:https://www.cnblogs.com/hujianglang/p/6260741.html
Copyright © 2011-2022 走看看