注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

对特化的新的理解  

2016-05-04 09:57:22|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

对特化的新的理解

重复一次,模板的实例化和全部特化、偏特化都是模板特化的一种;函数模板没有偏特化,因为那就是重载。但是只要不是显示指定模板参数,写了也没问题。

函数模板没有偏特化相关地址:

http://www.gotw.ca/publications/mill17.htm

http://blog.csdn.net/xuminggang/article/details/4333214

 

今天看一个百度上的问题:

http://zhidao.baidu.com/link?url=E23e0SS5HVvTpZFfgx9O_lA8fLG2STfkCTsUQrpFLtufygXDoNnLkPNApfeOlTykJwalRKsc-avXs47BrzJZAK

具体的定义在:

http://blog.csdn.net/imxiangzi/article/details/50264857

 

struct job

{

    char name[40];

    double salary;

    int floor;

};

template<typename T>

void Swap(T &t1,T &t2);

template<> void Swap<job>(job &c1,job &c2);

int main()

{

    double u, v;

    ...

    Swap(u,v); // use template

    job a, b;

    ...

    Swap(a,b); // use void Swap<job>(job &, job &)

}

 

其中Swap后面的那个<job>到底指定的是什么东西,Swap的类型和所有的参数不是都说明得很清楚了吗?

那直接声明一个原型

void Swap(job &,job &);不就可以了吗,何必大费周章写成这个

template <> void Swap<job>(job &, job &);

我最不理解的是Swap和参数列表之间的那个<job>到底是什么东西,swap的返回类型是void,接受两个job引用参数,这不是说得很清楚了吗.....

一句话引出今天的话题并回答上面的答案:

那个是函数模板的具现化,或者叫全特化,特化都可以。

 具现化是侯先生按台湾习惯的叫法来说的,咱们一般叫“实例化”这和本文一开始说的“都是模板的特化”是一致的,还有的人也叫“具体化”,都是一个意思,一定要分清。

在文章有时候会说“定义某个模板”其实也就是“实例化”的意思,大家在侯先生的书籍的时候儿或者原版英文的时候儿一定要搞清楚,把某个模板定义出来,可不就是实例化出来么。

与显式实例化不同的是,显式具体化使用下面的声明方式 ,两种方式是一样的

template<> void Swap<job>(job &c1,job &c2);

template<> void Swap(job &c1,job &c2);

 

class Test

{

public:

         Test(int t){ d = d + t; }

         int d = 338;

};

主模板类(泛化或者非特化)

template<typename T> void myswap(T&a, T&b){

         return b;

}

第一种

template<> void myswap(Test&a, Test&b)// template<> void myswap<Test> (Test&a, Test&b)

//注意,这两种方法是等效的,一个是显示特化,一个是自动推导

{

         b.d++;

         return ;

}

第二种

template<> void myswap(int &a, int& b)

{

         b = b+a;

}

第三种:

//void myswap<Test>(Test&a, Test&b)

//{

//      a.d++;

//}

注释的部分打开会报有重复的定义,如果注释掉的打开注释,并且把第一种注释掉,则程序会服显示特化使用的方式不正确。

 

如果主模板类改成如下:

template<typename T> T myswap(T&a, T&b){

         return b;

}

则这种会出问题

template<> T myswap(Test&a, Test&b)

{

         b.d++;

         return b ;

}

原因是类型T定义与实际不匹配,返回是T,结果是void,这样就不对了。

另外需要注意的是:

template void swap<int>(int &a, int & b);  //显式实例化,只需声明

这个只需要在声明里使用即可。

如果函数模板进行偏特化:

//函数模板偏特化实例

 

template<typename T, typename U>

inline int Count(T a, U b)

{

         return b;

}

template<typename T>

inline int Count<T,int>(T a, int b)

{

        

         return 1;

}

需要说明的是,显示特化(显式实例化,这也是显式实例化的目的,放到CPP文件去)函数模板,尽量不要放到头文件里,因为这样在多个CPP文件包含时,会报一个链接错误,解决的方法有三种:

http://blog.csdn.net/ztz0223/article/details/9699295

1、把特化的函数,添加inline标记,这样,编译器不会给这个函数生成一个函数符号,就当作是一个宏展开吧,不过,有些编译器不一定会inline的。行不行试试就知道了,如下:

[cpp] view plain copy print?

#ifndef HEADER 

#define HEADER 

 

template <class T> 

size_t size_rb_tree_node() 

    return 20; /*constant value for l r p pointer and (color & height) and void * value*/ 

 

template <> 

inline size_t size_rb_tree_node<void *>() 

    return 30; 

 

#endif 

2、让这个函数成为文件域,也就是不参与全局link,也是可以的:

[cpp] view plain copy print?

#ifndef HEADER 

#define HEADER 

 

template <class T> 

size_t size_rb_tree_node() 

    return 20; /*constant value for l r p pointer and (color & height) and void * value*/ 

 

template <> 

static size_t size_rb_tree_node<void *>() 

    return 30; 

 

#endif 

3、还有一个办法就是,把这个特化从头文件里面拿出去,放在需要的实现文件里面,再添加static属性。

另外,侯捷C++模板中P173页也做了相关说明。

就会报非法使用显式模板的参数的错误,如果去除显式的模板声明(红色部分,这叫做隐式特化),则可以编译通过,其实就是一个模板的重载了,而不是偏特化。

下面再给一个类模板的偏特化,大家要注意区别,不清楚的多看前面的BLOG或者找书看资料。这个确实有点儿理解上的小难度。

//类模板偏特化

template <typename T,typename U>

class TempClass

{

};

template <typename T>

class TempClass<T, int>

{

 

};

与上面的函数模板不同,类模板的全特化要尽量写在头文件与主模板类在一起,因为也是为了防止让编译出现无法找到错误的错误

所谓全特化就是 「某确凿之泛化 template 具现体」 因此同一个程序中不能同时存在一个 template

明确具现体」和一个由前者产生的具现体。如果同时使用两者,编译器会抓出你的小辫子:

template <typename T>

class Invalid {

};

Invalid<double> x1; // 引发 Invalid<double> 被实例化

template<>

class Invalid<double>; // 错误: Invalid<double> 已被实例化

不幸的是,如果你在不同的编译单元中这么写,编译器很难找出问题。 下面是一个非法的 C++

例子,由两个文件组成。它可以顺利通过很多编译器和链接器,但它不但非法而且危险:

// 编译单元 1

template<typename

T> class Danger

{ public:

enum { max = 10 };

};

char buffer[Danger<void>::max]; // 使用泛化值

172

extern void clear(char const*);

int main()

{

clear(buffer);

}

// 编译单元 2

template<typename T>

class Danger;

template<>

class Danger<void> {

public:

enum { max = 100 };

};

void clear(char const* buf)

{

// array 边界不匹配(不吻合)

for (int k = 0; k < Danger<void>::max; ++k) {

buf[k] = '\0';

}

}

这个例子刻意维持短小,但它说明:你必须小心确保「特化体的声明对泛化 template 的所有使

用者都可见」。现实而言,这意味特化体的声明通常应该在头文件中紧跟着其泛化 template

声明。但是当泛化实作源自一个外部源码文件(因此你无法修改其对应头文件), 上述

说法就不 太实际。不过你还是可以建立一个头文件,含入泛化 template,然后是其特化体的

声明,这就可 以避免这些难以发现的错误。我们发现,最好避免特化「外来源码所含的

templates」,除非对 方明确标示其设计目标就是如此

一点点的深入理解,深入学习。

 

 

 

  评论这张
 
阅读(162)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017