首页 > 代码库 > C++ Primer 学习笔记_77_模板与泛型编程 --实例化

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

模板与泛型编程

--实例化



引言:

模板是一个蓝图,它本身不是类或函数。编译器使用模板产生指定的类或函数的特定版本。产生模板的特定类型实例的过程称为实例化。

模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化。



1、类的实例化

当编写Queue<int>qi时,编译器自动创建名为Queue<int>的类。实际上,编译器通过重新编写Queue模板,用类型int代替模板形参的每次出现而创建Queue<int>类。实例化的类就像已经编写的一样:

template <typename Type> class Queue<Type>
{
public:
    Queue();
    int &front();
    const int &front() const;
    void push(const int &);
    void pop();
    bool empty() const;

private:
    //...
};

如果要为string类型的对象创建Queue类,可以编写:

	Queue<string> qs;

在这个例子中,用string代替Type的每次出现。

【重点理解:】

类模板的每次实例化都会产生一个独立的类类型:为int类型实例化的Queue与任意其他Queue类型没有关系,对其他Queue类型的成员也没有特殊访问权限!



2、类模板实参是必需的

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

    Queue qs;	//Error

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

    Queue<int> qi;
    Queue<string> qs;

用模板类定义的类型总是包含模板实参。例如,Queue不是类型,而Queue<int>Queue<string>是类型



3、函数模板实例化

使用函数模板时,编译器通常会为我们推断模板实参:

int main()
{
    compare(1, 0);	//将模板形参替换为int
    compare(3.14, 2.7);	//将模板形参替换为double
}

这个程序实例化了compare的两个版本:一个用int代替T,另一个用double代替T,实质上是编译器为我们编写了compare的这两个实例:

int compare(const int &v1, const int &v2)
{
    if (v1 < v2)
        return -1;
    if (v2 < v1)
        return 1;
    return 0;
}
int compare(const double &v1, const double &v2)
{
    if (v1 < v2)
        return -1;
    if (v2 < v1)
        return 1;
    return 0;
}

一、模板实参推断

要确定应该实例化哪个函数,编译器会查看每个实参。如果相应形参声明为类型形参的类型,则编译器从实参的类型推断形参的类型。从函数实参确定模板实参类型的过程叫做模板实参推断



1、多个类型形参的实参必须完全匹配

模板类型形参可以用作一个以上函数形参的类型。在这种情况下,模板类型推断必须为每个对应的函数实参产生相同的模板实参类型

template <typename T>
int compare(const T &val1,const T &val2)
{
    if (val1 < val2)
        return -1;
    else if (val2 < val2)
        return 1;
    return 0;
}

int main()
{
    short si;
    compare(si,1024);   //Error:调用compare的实参类型不同,无法完成正确推断
}

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

template <typename T,typename U>
int compare(const T &val1,const U &val2)
{
    if (val1 < val2)
        return -1;
    else if (val2 < val2)
        return 1;
    return 0;
}

int main()
{
    short si;
    compare(si,1024);   //OK
}

2、类型形参的实参的受限转换

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

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

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

    template <typename T>
    T fobj(T,T);
    template <typename T>
    T fref(const T &,const T &);

    string s1("a value");
    const string s2("another value");

    fobj(s1,s2);    //OK:call fobj(string,string),实参被复制
    fref(s1,s2);    //OK:cal fref(const string &,const string &)

    int a[10],b[42];
    fobj(a,b);  //OK:call fobj(int *,int *)
    fref(a,b);  //Error:实参没有办法转换成为pointer[指针]

3、应用于非模板实参的常规转换

注意下面的一段程序:

template <typename Type>
Type sum(const Type &op1,int op2)
{
    return op1 + op2;
}

因为op2的类型是固定的,在调用sum的时候,可以对传递给op2的实参应用常规转换:

    double d = 3.14;
    string s1("hiya"),s2("world");
    cout << sum(1024,d) << endl;
    cout << sum(1.4,d) << endl;
    cout << sum(s1,s2) << endl; //Error:没有从string到int的转换

4、模板实参推断与函数指针

可以使用函数模板函数指针进行初始化或赋值,此时,编译器使用指针的类型实例化具有适当模板实参的模板版本。

例如,假定有一个函数指针指向返回int值的函数,该函数接受两个形参,都是 constint 引用,可以用该指针指向compare的实例化

template <typename T>
int compare(const T &,const T &);
int (*pf1)(const int &,const int &) = compare;

pf1的类型是一个指针,指向“接受两个constint & 类型形参并返回int值的函数”,形参的类型决定了T的模板实参的类型T的模板实参为int,指针 pf1引用的是将 T绑定到 int的实例化。

获取函数模板实例化的地址的时候,上下文必须是这样的:它允许为每个模板形参确定唯一的类型或值。

如果不能从函数指针类型确定模板实参,就会出错:

void func(int (*)(const string &,const string &));
void func(int (*)(const int &,const int &));
func(compare);  //Error:通过查看func的形参类型不可能确定模板实参的唯一类型。

func的调用可以实例化下列函数中的任意一个:

	compare(const string&, const string&) 
	compare(const int&, const int&)

因为不能为传给func的实参确定唯一的实例化,该调用会产生一个编译时(或链接时)错误!

//P540 习题16.22
template <class Type>
Type calc(const Type *arr,int size);

template <class Type>
Type fcn(Type p1,Type p2);

int main()
{
    double dobj;
    float fobj;
    char cobj;
    int ai[5] = {511,16,8,64,343};

    calc(&cobj,‘c‘);
    calc(&dobj,fobj);
    fcn(ai,ai + sizeof(ai)/sizeof(*ai));
}