C++学习笔记12-模板1

 1.  函数模板

函数模板是一个独立于类型的函数,可作为一种方式,产生函数的特定类型版本。

// implement strcmp-like generic compare function

// returns 0 if thevalues are equal, 1 if v1 is larger, -1 if v1 is smaller

template <typename T>

int compare(const T&v1, const T &v2)

{

if (v1 < v2)return -1;

if (v2 < v1)return 1;

return 0;

}

2. 类模板

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

template <class Type> class Queue {

public:

Queue ();                             // default constructor

Type &front ();                     // return element from head of Queue

const Type &front() const;

void push (const Type&);   // add element to back of Queue

void pop();                            // remove element from head of Queue

bool empty() const;              // true if no elements in the Queue

private:

// ...

};

3. 模板形参

像函数形参一样,程序员为模板形参选择的名字没有本质含义。在我们的例子中,将compare 的模板类型形参命名为T,但也可以将它命名为任意名字:

// equivalent template definition

template <class Glorp>

int compare(const Glorp &v1, const Glorp &v2)

{

if (v1 < v2)return -1;

if (v2 < v1)return 1;

return 0;

}

该代码定义的compare 模板与前面一样。可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类型形参。如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。

4. 模板形参作用域

模板形参的名字可以在声明为模板形参之后直到模板声明或定义的末尾处使用。模板形参遵循常规名字屏蔽规则。与全局作用域中声明的对象、函数或类型同名的模板形参会屏蔽全局名字:

typedef double T;

template <class T> T calc(const T &a, const T &b)

{

// tmp has the typeof the template parameter T

// not that of the global typedef

T tmp = a;

// ...

return tmp;

}

将T 定义为double 的全局类型型别名将被名为T 的类型形参所屏蔽,因此,tmp不是double 型,相反,tmp的类型是绑定到模板形参的任意类型。使用模板形参名字的限制用作模板形参的名字不能在模板内部重用。

template <class T> T calc(const T &a, const T &b)

{

typedef double T; //error: rede clares template parameter T

T tmp = a;

// ...

return tmp;

}

这一限制还意味着模板形参的名字只能在同一模板形参表中使用一次:

// error: illegalre use of template parameter name V

template <class V,class V> V calc(const V&, const V&) ;

当然,正如可以重用函数形参名字一样,模板形参的名字也能在不同模板中重用:

// ok: reuses parameter type name across different templates

template <class T> T calc (const T&, const T&) ;

template <class T> int compare(const T&, const T&) ;

5. 模板声明

像其他任意函数或类一样,对于模板可以只声明而不定义。声明必须指出函

数或类是一个模板:

// declares compare but does not define it

template <class T> int compare(const T&, const T&) ;

同一模板的声明和定义中,模板形参的名字不必相同。

// all three uses of calc refer to the same function template

// forward declarations of the template

template <class T> T calc(const T&, const T&) ;

template <class U> U calc(const U&, const U&) ;

// actual definitionof the template

template <class Type>

Type calc(const Type& a, const Type& b) { /* ... */ }

每个模板类型形参前面必须带上关键字 classtypename,每个非类型形参前面必须带上类型名字,省略关键字或类型说明符是错误的:

// error: must precede U by either typename or class

template <typename T, U> T calc (const T&, const U&) ;

6. typename 与 class 的区别

在函数模板形参表中,关键字typename 和class 具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用:

// ok: no distinction between typename and class in template parameter list

template <typename T, class U> calc (const T&, const U&);

使用关键字typename 代替关键字class 指定模板类型形参也许更为直观,毕竟,可以使用内置类型(非类类型)作为实际的类型形参,而且,typename更清楚地指明后面的名字是一个类型名。但是,关键字typename 是作为标准C++ 的组成部分加入到C++ 中的,因此旧的程序更有可能只用关键字class。

7. 编写泛型代码的两个重要原则:

? 模板的形参是const 引用。

? 函数体中的测试只用< 比较。

8. 类模板形参是必需的

想要使用类模板,就必须显式指定模板实参:

Queue qs; // error:which template instantiation?

类模板不定义类型,只有特定的实例才定义了类型。特定的实例化是通过提供模板实参与每个模板形参匹配而定义的。模板实参在用逗号分隔并用尖括号括住的列表中指定:

Queue<int> qi;// ok: defines Queue that holds ints

Queue<string>qs; // ok: defines Queue that holds strings

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

考虑下面的compare 调用:

short s1, s2;

int i1, i2;

compare(i1, i2);     //ok: instantiate compare(int, int)

compare(s1, s2);  //ok: instantiate compare(short, short)

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

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

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

例如,考虑对函数fobj 和fref 的调用。fobj函数复制它的形参,而fref

的形参是引用:

template <typename T> T fobj(T, T); // arguments are copied

template <typename T> T fref(const T&,const T&); // reference arguments

string s1("avalue");

const strings2("another value");

fobj(s1, s2);       // ok:calls f(string, string), const is ignored

fref(s1, s2);        // ok: non const object s1 converted to const reference

int a[10], b[42];

fobj(a, b);          // ok:calls f(int*, int*)

fref(a, b);           // error:array types don‘t match; arguments aren‘t converted to pointers

第一种情况下,传递string 对象和const string 对象作为实参,即使这些类型不完全匹配,两个调用也都是合法的。在fobj 的调用中,实参被复制,因此原来的对象是否为const 无关紧要。在fref 的调用中,形参类型是const引用,对引用形参而言,转换为const 是可以接受的转换,所以这个调用也正确。

在第二种情况中,将传递不同长度的数组实参。fobj的调用中,数组不同无关紧要,两个数组都转换为指针,fobj的模板形参类型是int*。但是,fref的调用是非法的,当形参为引用时数组不能转换为指针,a和b 的类型不匹配,所以调用将出错。

10. 类型转换的限制只适用于类型为模板形参的那些实参。

时间: 2024-10-09 22:52:46

C++学习笔记12-模板1的相关文章

C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模板的定义. 泛型编程与面向对象编程一样,都依赖于某种形式的多态性.面向对象编程中的多态性在执行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异.仅仅要使用基类的引用或指针,基类类型或派生类类型的对象就能够使用同样的代码. 在泛型编程中,我们所编写的类和函数能够

OpenCV 学习笔记(模板匹配)

OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够高时,就认为找到了我们的目标. 在 OpenCV 中,提供了相应的函数完成这个操作. matchTemplate 函数:在模板和输入图像之间寻找匹配,获得匹配结果图像 minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置 在具体介绍这两个函数之前呢,我们还要介绍一个概念,就是如何来评价两

C++学习笔记之模板(1)——从函数重载到函数模板

一.函数重载 因为函数重载比较容易理解,并且非常有助于我们理解函数模板的意义,所以这里我们先来用一个经典的例子展示为什么要使用函数重载,这比读文字定义有效的多. 现在我们编写一个交换两个int变量值得函数,可以这样写: 1 void swap(int & a, int & b) 2 { 3 int tmp; 4 5 tmp = a; 6 a = b; 7 b = tmp; 8 9 } 假如我们现在想在与上面函数相同的文件中(可以理解为同一个main函数中)交换两个float变量呢,是不是需

C++ Primer 学习笔记_77_模板与泛型编程 --实例化

模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化. 1.类的实例化 当编写Queue<int>qi时,编译器自己主动创建名为Queue<int>的类.实际上,编译器通过又一次编写Queue模板,用类型int取代模板形參的每次出现而创建Queue<int

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一些情况下,能够利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数. compare函数和 Queue类都是这一问题的好样例:与C风格字符串一起使用进,它们都不能正确工作. compare函数模板: template <typename Type> int compare(cons

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下

C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还可以只特化push和pop成员.我们将特化push成员以复制字符数组,并且特化pop成员以释放该副本使用的内存: template<> void Queue<const char *>::push(const char *const &val) { char *new_item = new char[sizeof(val) + 1]; strncpy(new_item,val,sizeof(

C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型

模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不立即产生代码.只有在用到模板时,如果调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调用函数时[不是模板],编译器只需看到函数的声明.类似的,定义类类型的对象时,类定义必须可用,但成员函数的定义不是必须存在的.因此,应该将类定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中. 模板则不同:要进行实例化,编译器必须能够访问定义模板的源代码.当调用函数模板或类模板的成员函