• 模板


    函数模板

    1.定义函数模板:模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔,模板形参表不能为空。比如:

    1 template <typename T>
    2      int compare(const T &v1, const T &v2)
    3      {
    4          if (v1 < v2) return -1;
    5          if (v2 < v1) return 1;
    6          return 0;
    7      }

    模板形参表很像函数形参表,函数形参表定义了特定类型的局部变量但并不初始化那些变量,在运行时再提供实参来初始化形参。模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。非类型形参跟在类型说明符之后声明,类型形参跟在关键字 classtypename 之后定义,例如,typenameT 是名为 T 的类型形参。

    2.使用函数模板时,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。编译器承担了为我们使用的每种类型而编写函数的单调工作。比如:

     1      int main ()
     2      {
     3          // T is int;
     4          // compiler instantiates int compare(const int&, const int&)
     5          cout << compare(1, 0) << endl;
     6          // T is string;
     7          // compiler instantiates int compare(const string&, const string&)
     8          string s1 = "hi", s2 = "world";
     9          cout << compare(s1, s2) << endl;
    10          return 0;
    11      }

    编译器将实例化 compare 的两个不同版本,编译器将用 int 代替 T 创建第一个版本,并用 string 代替 T 创建第二个版本。

    3.inline 函数模板

    函数模板可以用与非模板函数一样的方式声明为 inline说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前

    1  // ok: inline specifier follows template parameter list
    2      template <typename T> inline T min(const T&, const T&);
    3      // error: incorrect placement of inline specifier
    4      inline template <typename T> T min(const T&, const T&);

    4.模板实参推断

    要确定应该实例化哪个函数,编译器会查看每个实参。如果相应形参声明为类型形参的类型,则编译器从实参的类型推断形参的类型。在 compare 的例子中,两个实参有同样的模板类型,都是用类型形参 T 声明的。

    第一个调用 compare(1, 0) 中,实参为 int 类型;第二个调用 compare(3.14, 2.7) 中,实参为 double 类型。从函数实参确定模板实参的类型和值的过程叫做模板实参推断

    模板类型形参可以用作一个以上函数形参的类型。在这种情况下,模板类型推断必须为每个对应的函数实参产生相同的模板实参类型。如果推断的类型不匹配,则调用将会出错:

     1      template <typename T>
     2      int compare(const T& v1, const T& v2)
     3      {
     4          if (v1 < v2) return -1;
     5          if (v2 < v1) return 1;
     6          return 0;
     7      }
     8      int main()
     9      {
    10          short si;
    11          // error: cannot instantiate compare(short, int)
    12          // must be: compare(short, short) or
    13          // compare(int, int)
    14          compare(si, 1024);
    15          return 0;
    16      }

    如果想要允许实参的常规转换,则函数必须用两个类型形参来定义:

    1      template <typename A, typename B>
    2      int compare(const A& v1, const B& v2)
    3      {
    4          if (v1 < v2) return -1;
    5          if (v2 < v1) return 1;
    6          return 0;
    7      }

    这样上面的调用就没有问题了,用于比较的类型必须支持<比较。如果是类类型该类必须实现重载<。

    5.类型形参的实参的受限转换

    一般而论,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器只会执行两种转换:

    (1)const 转换:接受 const 引用或 const 指针的函数可以分别用非 const 对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型实参都忽略 const,即,无论传递 const 或非 const 对象给接受非引用类型的函数,都使用相同的实例化。

    (2)数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。

     1 template <typename T> T fobj(T, T); // arguments are copied
     2      template <typename T>
     3      T fref(const T&, const T&);       // reference arguments
     4      string s1("a value");
     5      const string s2("another value");
     6      fobj(s1, s2);     // ok: calls f(string, string), const is ignored
     7      fref(s1, s2);     // ok: non const object s1 converted to const reference
     8      int a[10], b[42];
     9      fobj(a, b); // ok: calls f(int*, int*)
    10      fref(a, b); // error: array types don't match; arguments aren't converted to pointers

    类模板

    1.定义类模板

    类模板也是模板,因此必须以关键字 template 开头,后接模板形参表。Queue 模板接受一个名为 Type 的模板类型形参。比如:

     1      template <class Type> class Queue {
     2      public:
     3          // empty Queue
     4          Queue(): head(0), tail(0) { }
     5          // copy control to manage pointers to QueueItems in the Queue
     6          Queue(const Queue &Q): head(0), tail(0)
     7                                        { copy_elems(Q); }
     8          Queue& operator=(const Queue&);
     9          ~Queue() { destroy(); }
    10               // return element from head of Queue
    11          // unchecked operation: front on an empty Queue is undefined
    12          Type& front()             { return head->item; }
    13          const Type &front() const { return head->item; }
    14          void push(const Type &);       // add element to back of Queue
    15          void pop ();                    // remove element from head of Queue
    16          bool empty () const {           // true if no elements in the Queue
    17              return head == 0;
    18          }
    19      private:
    20          QueueItem<Type> *head;         // pointer to first element in Queue
    21          QueueItem<Type> *tail;         // pointer to last element in Queue
    22          // utility functions used by copy constructor, assignment, and destructor
    23          void destroy();                // delete all the elements
    24          void copy_elems(const Queue&); // copy elements from parameter
    25      };

    除了模板形参表外,类模板的定义看起来与任意其他类问相似。类模板可以定义数据成员、函数成员和类型成员,也可以使用访问标号控制对成员的访问,还可以定义构造函数和析构函数等等。在类和类成员的定义中,可以使用模板形参作为类型或值的占位符,在使用类时再提供那些类型或值。

    2.使用类模板

    与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参:

    1      Queue<int> qi;                 // Queue that holds ints
    2      Queue< vector<double> > qc;    // Queue that holds vectors of doubles
    3      Queue<string> qs;              // Queue that holds strings

    3.类模板函数实现方式:

    template <class Type> returnType  classTemplate::(arg)比如:

    1      template <class Type> void Queue<Type>::destroy()
    2      {
    3          while (!empty())
    4              pop();
    5      }

    4.何时实例化类和成员

    类模板的成员函数只有为程序所用才进行实例化。如果某函数从未使用,则不会实例化该成员函数。这一行为意味着,用于实例化模板的类型只需满足实际使用的操作的要求。定义模板类型的对象时,该定义导致实例化类模板。定义对象也会实例化用于初始化该对象的任一构造函数,以及该构造函数调用的任意成员,比如:

    1     // instantiates Queue<int> class and Queue<int>::Queue()
    2      Queue<string> qs;
    3      qs.push("hello"); // instantiates Queue<int>::push

    5. 类模板中的友元声明

    在类模板中可以出现三种友元声明,每一种都声明了与一个或多个实体友元关系:

    (1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。

    (2)类模板或函数模板的友元声明,授予对友元所有实例的访问权。

    (3)只授予对类模板或函数模板的特定实例的访问权的友元声明。

    6.类模板的 static 成员

    (1)类模板static成员,为实例化的类共享

    (2)类模板static成员使用的时候必须实例化

    (3)像使用任意其他 static 数据成员一样,必须在类外部出现数据成员的定义。在类模板含有 static 成员的情况下,成员定义必须指出它是类模板的成员:

        template <class T>
         size_t Foo<T>::ctr = 0; // define and initialize ctr

    7. 模板特化

    1.为什么要模板特化:某些情况下,通用模板定义对于某个类型可能是完全错误的,通用模板定义也许不能编译或者做错误的事情;另外一些情况下,可以利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数。

    2.函数模板特化

    template<>

    returnType funName<teplate agruments>(arguments)

    {

        //something should do

    }

    比如:

        template <>
         int compare<const char*>(const char* const &v1,
                                  const char* const &v2)
         {
             return strcmp(v1, v2);
         }

    特化的声明必须与对应的模板相匹配。在这个例子中,模板有一个类型形参和两个函数形参,函数形参是类型形参的 const 引用,在这里,将类型形参固定为 const char*,因此,函数形参是 const char*const 引用。

     3.类模板特化

    (1)类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪。

     (2)在类特化外部定义成员时,成员之前不能加 template<> 标记。

     

  • 相关阅读:
    EL表达式遍历方法之一
    django auth认证
    总结五个在办公中使用很爽的软件
    django所遇到问题简单总结
    django 远程数据库mysql migrate失败报error 1045之 解决方案
    深拷贝和浅拷贝之地址改变
    序列表转换成横向菜单
    Pycharm破解
    css里涉及到定位相关的example实例
    记录求职
  • 原文地址:https://www.cnblogs.com/xiaofeifei/p/3355987.html
Copyright © 2020-2023  润新知