• 模版与泛型编程


    非类型模版参数

      非类型参数表示一个值而非一个类型,通过一个特定的类型名而非关键字typename或者class来指定非类型参数。当一个模版被实例化时,非类型参数被一个用户提供或者编译器推断出的值所代替。这些值必须是常量表达式,从而允许编译器在编译时实例化。(关键在于编译器的自动推断)

      例如编写一个compare版本用来处理字符字面常量。这种字面常量是const char的数组。由于不能拷贝数组,所以我们将自己的参数定义为数据的引用。由于我们希望比较不同长度的字符字面常量,因此为模版定义了两个非类型的参数。第一个模版参数表示第一个数组的长度,第二个参数表示第二个数组的长度。

    template<unsigned N, unsigned M>
    int compare(const char (&p1)[N], const char (&p2)[M])
    {
          return strcmp(p1, p2);    
    }

      当我们调用compare("hi", "mom")的时候,实际上实例化出如下版本:

    int compare(const char (&p1)[3], const char (&p2)[4])

     模版编译

      为了实例化一个模版,编译器需要掌握模版的声明以及定义,也就是模版的头文件通常既包括声明也包含定义。

       在一个类模版的作用域内, 可以直接使用模版名而不必指定模版实参。

    类模版和友元素 

      

      上图中的前置声明要注意下咯,模版类的前置声明的形式(template <typename> class Blobptr)。

      友元的声明用Blob的模形参作为它们自己的模版实参。因此,友好关系被限定在用相同类型实例化的Blobptr与Blob相等运算符中: 

    Blob<char> ca; // BlobPtr<char> 和 operator==<char>为本对象的友元

    通用和特定的模板友好关系

       为了让所有实例成为友元,友元声明中必须使用于类模版本身不同的模版参数。

    令模版自己的类型参数成为友元

    template <typename Type> class Bar {
         friend Type;   //将访问权限授予用来实例化Bar的类型  
    }
    

      对于某个类型名FOO而言,FOO将成为Bar<Foo>的友元。(静态成员可以通过对象或者作用域运算符调用,静态成员除了声明之外,必须有唯一的定义。) 

      当我们希望通知编译器一个名字表示类型的时候,必须使用关键字typename,而不能使用class。

     模版默认实参与类模版

    类模板的成员模板

      

     

      在类模版外定义成员模版的时候,必须同时为类模版以及成员模版提供模版参数列表(类模版在前,成员模版在后)。 

    控制实例化

      当两个或多个独立编译的源文件使用了相同的模版,并提供了相同的模版参数时,每个文件都会有该模版的一个实例。对于大文件系统而言,在多个文件中实例化相同模板带来的额外开销会非常严重,可以通过显示实例化来解决这一问题:

    extern template declaration; // 实例化声明
    template declaration; // 实例化定义
    
    // e.g
    extern template class Blob<string>;  // 声明
    template int compare(const int&, const int&); // 定义

     当编译器遇到extern模版声明时,它不会在本文件中生成实例化的代码,而是从程序其他位置找到该实例化的一个非extern定义(声明)。例如:

     

       Application.cc中extern声明的模版就不会在本文件实例化,但是必须能够在其他文件中找到非extern的定义。

      一个类模版的实例化定义会实例化该模版的所有成员,包括内联的成员函数,显示实例化一个类模版的类型,必须能够用于模版的所有成员。

     模版实参推断

      从函数实参来确定模版实参的过程被称之为模版实参推断。如果一个函数形参的类型使用了模版类型参数,那么它将采用特殊的初始化规则。编译器通常不是对实参进行类型转换,而是生成一个新的模版实例。有以下规则:

      1. 顶层const会被忽略(常量指针,很好理解,由于是生成新的实例,自然指向的对象是可以改变的)。

      2. 可以将一个非const的引用对象(或指针)传递给一个const的饮用(或指针)形参。

      3. (形参非引用类型)一个数组实参可以转换为一个指向其首元素的指针;一个函数实参可以转换为一个该函数类型的指针。

    函数显式实参

      如下函数定义:

    template <typename T1, typename T2, typename T3>
    T1 sum(T2, T3);

      sum函数没有任何函数实参可以用来推断T1,因此每次使用sum时必须为T1提供一个显示模版参数,如下:

    auto val3 = sum<long long>(i, lng); // long long sum(int, long)

      注意,显示模版实参按由左至右的顺序与对应的模版参数匹配。模版类型参数已经显示指定了的函数实参,可以进行正常的类型转换。

    尾置返回类型与类型转换

      如下函数:

       我们并不知道返回结果的准确类型,但知道是所需类型是所处理的序列的元素类型。可以考虑尾置返回类型结合decltype来确定类型。

       如果我们想利用fcn返回元素的拷贝,我们需要利用remove_reference来获得元素类型。我们用

    remove_reference<decltype(*debug)>::type

      获得beg引用的元素类型,改进的函数如下:

       注意,这里的type是一个类的成员,该类依赖于一个模版参数,需要用typename显示的告诉编译器这是一个类型。

    引用折叠和右值引用参数

      将一个左值传递给函数的右值引用参数,且此右值引用指向模版类型参数(如T&&),编译器推断模版类型参数为实参的左值引用类型。

      引用折叠只能应用于间接创建的引用的引用,如类型别名或模版参数。

      尽管static_cast只能用于其他合法的类型转换,但有个例外:可以用static_cast显式的将一个左值转换为一个右值引用。 

     转发(std::forward)(完美保持原有的类型)

      如果一个函数参数是指向模板类型参数的右值引用,它对应的实参的const属性和右值/左值属性将得到保持。

      当用于一个指向模版参数类型的右值引用函数参数(T&&)时,std::forward会保持实参的所有细节

    函数重载与模版

    可变参数模板 

      一个可变参数模版就是一个接受可变数目参数的模版函数/类。可变数目的参数称为参数包。可以通过sizeof运算符来计算包中有多少元素。

    644)

  • 相关阅读:
    Java开发笔记(四十二)日历工具的常见应用
    Java开发笔记(四十一)日历工具Calendar
    Java开发笔记(四十)日期与字符串的互相转换
    Java开发笔记(三十九)日期工具Date
    Java开发笔记(三十八)利用正则表达式校验字符串
    Java开发笔记(三十七)利用正则串分割字符串
    Java开发笔记(三十六)字符串的常用方法
    Java开发笔记(三十五)字符串格式化
    Git 工作流程
    如果你恨一个程序员,忽悠他去做iOS开发
  • 原文地址:https://www.cnblogs.com/z1141000271/p/15013946.html
Copyright © 2020-2023  润新知