模板实例化

如果说上一篇博文《模板名称》是教人怎么写模板,那么这一篇就是教人怎么使用模板。

模板实例化的复杂性在于:对于产生自模板的实体,它们的定义已经不再局限于源代码中的单一位置。

一、理解两个概念

(1)实例化:实例化在C++中通常指“根据类型创建一个对象”,但是在模板里面,实例化是指使用具体值替换模板实参,从模板中产生普通类,函数或者成员函数过程

(2)特化:这个过程最后获得的试题就是我们所说的特化。

然而,实例化过程并不是特化的唯一方式,还有显式特化,通过引入一个template<>来实现,如下:

template<typename T1, typename T2>

class MyClass{};

template<>

class MyClass<std::string, float>{};

二、按需实例化(on-demand实例化)

如果(某个组件)期望知道模板特化的大小,或者访问该特化的成员,那么整个定义就需要位于作用域中。

比如显示的调用模板的成员,或者是包含隐式转换

(1)显示调用成员函数

template<typename T> class C;   //前置声明

C<int>* p = 0; //这里只需要声明就够了

template<typename T>

class C{

public:

void f();

};

void g(C<int>& c){

c.f();                    //此处需要知道整个模板的定义,因为编译器要确定f()是不是可以被访问到

}

(2)隐式类型转换

C++重载规则要求:如果候选函数的参数是class类型,那么该类型所对应的类就必须可见

template <typename T>

class C{

public:

C(int);  //单参数隐式类型转换

};

void candidate(C<double> const&); //①允许编译器实例化该重载函数,但不是必须的,在VS2013中,就没有实例化参数

void candidate(int){}                        //②

int main()

{

candidate(42);         //编译器不会选择①处的声明,因为一个精确的匹配要优于显式转型所获得的匹配

return 0;

}

三、延迟实例化

编译器只对确实需要的部分实例化。换句话说,编译器会延迟模板的实例化。

(1)当隐式实例化类模板时,同时也实例化了该模板的每一个成员函数的声明,但并没有实例化相应的定义。

但是有些情况是不会延迟的,如下:

①类模板里面包含有匿名的union,那么,匿名的union成员同时也被实例化,

②虚函数,作为实例化类模板的结果,许多编译器实现都会实例化虚函数的定义,因为“实现虚函数调用机制的内部结构”要求虚函数的定义作为链接实体的存在。

(2)实例化类模板与实例化缺省的函数调用实参是分开的。换句话说,只有函数确实使用了缺省的实参,才会实例化该实参,如果这个函数不使用缺省的实参,那么就不会实例化该缺省的实参,而是显式使用实参来实例化。

template<typename T>

class Safe{};

template<int N>

class Danger{

typedef char Block[N];            //如果N<=0的话,将会出错

};

template<typename T, int N>

class Tricky{

public:

virtual ~Tricky(){}    //虚函数,并提供了定义
    void no_body_here(Safe<T>=3);     //该缺省实参是可疑的,但没有被使用,不会被实例化,不会出错
    void inclass(){
        Danger<N>no_boom_yet;          //没有被使用,不会被实例化,不会出错

}

//void error(){ Danger<0> boom;}      //如果没有被注释,会被要求给出这个类Danger<0>的完整定义,
//而实例化Danger<0>会出错,即使没有被使用,也不会被实例化,但仍然能够引发一个错误
//该错误是在泛模板处理中产生的
//void unsafe(T(*p)[N]);        //如果 没有注释掉的话,此处实例化声明的时候会出错

T operator->();

//virtual Safe<T> suspect();     //虚函数,但是没有提供定义,所以会引发一个链接期的错误,

//如果不注释掉的话,链接器就会给出这类错误

struct Nested{
    Danger<N> pfew;      //因为没有使用该结构,所以此处的没有实例化

};

union{

int align;

Safe<T> anonymous;

};

};

int main()

{

Tricky<int, 0> ok;

}

三、C++实例化模型

(1)两阶段查找

第一阶段:使用普通查找规则(在适当情况也会使用ADL)对模板进行解析,查找非依赖型名称。另外非受限的依赖型名称(诸如函数调用中的函数名称,因为其具有一个依赖型实参)也会在这个阶段查找,只不过查找不完全,在实例化模板的时候还会再次进行查找。

第二阶段:发生在模板被实例化的时候,我们也称此时发生的地点(或源代码的某个位置)为一个实例化点POI。依赖型受限名称就在此时查找。另外,非受限的依赖型名称在此阶段也会再次执行ADL查找.

(2)POI(实例化点)

①对于指向非类型(也就是函数╮( ̄▽ ̄")╭)特化的引用, C++把他的POI定义在“包含这个引用定义或声明之后的最近名字空间域”。

class MyInt{

public:

MyInt(int i);

};

MyInt operator - (MyInt const);

bool operator >(MyInt const&, MyInt const&);

typedef MyInt int;    //②

template <typename T>

void f(T i)

{

if(i>0){

g(-i);   //①

}

}

//(1)

void g(Int)    //这就是那个定义或声明

{

//(2)

f<Int>(42); //这就是那个引用

//(3)

}

//(4)这就是那个“之后最近的名字空间域”,函数f<int>的一个特化会出现在这里

 

【注意】①位置的名称g, 是非受限依赖型名称,因为他的参数是依赖型的, 所以会在第二阶段查找只是使用ADL就能够找到函数g(Int)其实也就是g(MyInt);如果将MyInt替换成int,即②处为typedef int Int,那么第二阶段的查找关联命名空间就会是空集,也就找不到函数g(Int)的声明和定义。

②对于产生自模板的类实例的引用,它的POI只能定义在“包含这个实例引用定义或声明之前的最近名字空间域”。

template<typename T>

class S{

public:

T m;

};

//(5) 这里就是S的POI

unsigned long h() //这就是那个定义或声明

{

//(6)

return (unsigned long) sizeof(S<int>);  //这就是那个实例引用

//(7)

}

//(8)

四、显式实例化

为模板特化显式的生成POI是可行的,我们把这种特化的构造成为显式实例化指示符。从语法关键字上讲,它有关键字template和后面的特化声明组成,所声明的特化就是即将有实例化获得的特化。

template<typename T>

void f(T) throw(T){}

下面有4个有效的显式实例化实体

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

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

template void f(int) throw(int); //通过演绎获得

template void f(int); //异常规范也可以省略,如果没有省略,异常规范必须匹配相应的模板

C++规定: 同一个程序中,每一个特定的模板特化最多只能存在一处显式实例化。而且,如果摸个模板特化已经被显式实例化(使用template),那么就不能对其进行显式特殊化(使用template<>)。

时间: 2024-11-07 08:23:59

模板实例化的相关文章

C++模板实例化

深入理解C++中第七章提到模板实例化参数的选择:函数的决议结果只和函数参数有关和返回值无关.记录一下. 测试程序如下: #include <iostream> using namespace std; int foo(int d) { cout << "int foo" << endl; return d+1; } double foo(double d) { cout << "double foo" <<

函数申明对函数模板实例化的屏蔽

1.C++函数匹配顺序 C++语言引入模板机制后,函数调用的情形显的比C语言要复杂.当发生一次函数调用时,如果存在多个同名函数,则C++编译器将按照如下的顺序寻找对应的函数定义. (1)寻找一个参数完全匹配的函数,如果找到了就调用它. (2)寻找一个函数模板,并根据调用情况进行参数推演,如果推演成功则将其实例化,并调用相应的模板函数. (3)如果前面两种努力都失败了,则试着低一级的函数匹配方法,如通过类型转换能否达到参数匹配,如果可以,则调用它. 2. 函数申明对函数模板实例化的屏蔽 如果使用了

编译模板实例化

如果头文件中共有模板的定义,而且该头文件被多处包含,就会为该模板创建多个编译好的副本.这样做当然可行,但在含有多个模板的大程序中,所有目标文件都会包含每个编译好的模板副本,这样会令编译时间变得很长,而且目标文件很大, 下面是一些可选的方案: 1.在源文件中,使用指示字#pragma  interface 和#pragma implementation,就只会创建一个编译后的模板. 2.与使用两个prama相似,使用命令行选项-falt-external-templates编译所有源文件.这会告诉

SQL模板和模板实例化

需求:需要得出一个数据源DataTable,我已知SQL和HttpRequest如何,通过SQL模板的方式去实例化匹配HttpRequest中的参数实例化为查询SQL,最后返回DataTable 1.SQL模板 1 SELECT 2 SUBSTR(PLACENAME, instr(PLACENAME, '-') + 1) 场景名, 3 COUNT(*) 小区数, 4 SUM(ALARMCOUNT) 流量TB, 5 ROUND(SUM(USERCOUNT)) 在线用户, 6 SUM(TOTALFL

MOOC清华《面向对象程序设计》第8章:栈的实现(采用模板实例化)

3lztmo1xf豪已4兹6http://www.zcool.com.cn/collection/ZMTkxNTY1OTI=.html 6桓50劳爻拍62诽喜0栽2http://www.zcool.com.cn/collection/ZMTkxNTY3MDQ=.html yc恍6焊eu桌难4gwmhttp://www.zcool.com.cn/collection/ZMTkxNTY3MjQ=.html Oq68厦3难钟0献qhttp://www.zcool.com.cn/collection/Z

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

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

C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解

代码编译运行环境:VS2012+Debug+Win32 模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程.对于函数模板而言,模板实例化之后,会生成一个真正的函数.而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始化.模板的实例化分为隐式实例化和显示实例化. 对函数模板的使用而言,分为两种调用方式,一种是显示模板实参调用(显示调用),一种是隐式模板实参调用(隐式调用).对于类模板的使用而言,没有隐式模板实参和显式模板实参使用的说法,因为类模板的使用必须显

C++ template —— 实例化和模板实参演绎(四)

本篇讲解实例化和模板实参演绎------------------------------------------------------------------------------------------------------------第10章 实例化------------------------------------------------------------------------------------------------------------模板实例化是一个过程,它

C++ 模板与泛型编程

<C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异. 在泛型编程中,我们所编写的类和函数能够多态地用于跨越编译时不相关的类型.一个类或一个函数可以用来操纵多种类型的对象. 面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数式多态性. 模板是泛型