欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++模板特化和偏特化

程序员文章站 2024-01-14 17:31:40
...

1、模板特化的引入

        使用模板时会遇到一些特殊的类型需要特殊处理,不能(或不希望)直接使用当前的模板版本时,所以此时我们就需要对该类型定义一个类或函数的特例化版本。

例:当使用一个判断相等的模板函数时

template<class T>
bool Isequal(T& p1, T& p2){
	return p1 == p2;
}

       但是该模板函数在对于字符串进行比较时就不能使用了,对于字符串我们不能直接比较,因此直接特化出一个专门供字符串使用的模板参数:

template<> 								// 此处不添加类型模板,直接使用空即可
bool Isequal<char*>(char*& p1, char*& p2){	// 在类名后使用尖括号注明特化类型
	return strcmp(p1, p2) == 0;
}

【注意】

(1)模板及其特例化版本应该声明在同一个头文件中。所有同名模板的声明应该放在前面,然后是这些模板的特例化版本

(2)使用特换模板函数时格式有要求:

  • template 后直接跟<> 里面不用写类型
  • 函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名后跟<>其中写要特化的类型

(3)特化的函数的函数名,参数列表要和原基础的模板函数相同,避免不必要的错误。

(4)特例化版本本质上是一个实例,而非函数名的一个重载版本。

(5)当一个非模板函数提供与函数模板同样好的匹配时,编译器会选择非模板版本。

(6)如果丢失了一个特例化版本声明,编译器通常用原模板生成代码。由于在丢失特化版本时编译器通常会实例化原模板,很容易产生模板及其特例化版本声明顺序导致错误,而这种错误又很难查找。

 

2、特化类型

        模板特化分为全特化和偏特化(对模板的类型做一些限制),其中全特化适用于函数模板和类模板,偏特化只适用于类模板。

        对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序是:全特化类>偏特化类>主版本模板类。这样的优先级顺序对性能也是最好的。

(1)全特化

        全特化: 将所有的模板类型都进行特化。

例:

// 普通模板
template <class T1, class T2>
class Test{
}

// 全特化模板
template <>  			// 此处同函数模板的特化一样不用写内容
class Test<int , char>{	// 类名后尖括号中指出特化类型
    
}

(2)偏特化

        偏特化分为两种:一种是部分特化,另一种是对模板类型的进一步限制。

例:部分特化

// 普通类模板
template <class T1, class T2>
class Test2{
}

// 部分特化类模板
template <class T1>  	// 此处只需写未进行特化的模板类型,特化过的就不用写
class Test2<T1 , char>{	// 类名后的尖括号中包含普通模板参数和特化模板参数
    
}

 

例:对模板类型的范围的限制,主要的基础类型不变

// 普通模板
template <class T1, class T2>
class Test2{
}

// 对模板类型的范围做出一定的限制
template <class T1 , class T2 >  	// 此处只需写未进行特化的模板类型
class Test2<T1* , T2*>{			// 限制模板参数为指针类型
}

例:

// 原始的、最通用的版本
template <class T> struct remove_reference{
typedef  T type; 
};

// 部分特化版本,用于左值引用和右值引用
template <class T> struct remove_reference<T &>{	// 左值引用
	typedef  T type; 
};
template <class T> struct remove_reference<T &&>{	// 右值引用
	typedef  T type; 
};

// 模板的调用
int i;
// decltype(42)为int,使用原始模板
remove_reference<decltype(42)>::type a;
// decltype(i)为int&,使用第一个(T&)部分特化版本
remove_reference<decltype(i)>::type b;
// decltype(std::move(i)为int&&,使用第二个(T &&)部分特化版本
remove_reference<decltype(std::move(i))>::type c;

 

3、特化类中部分成员而非整类

        我们可以只特化类中特定成员函数而非特化整个模板。

例:只特化Foo中的Bar

template <typename T> struct Foo{
Foo(const T &t = T()):met() {}
void Bar() {}
T met;
};

template<>
void Foo<int>::Bar(){}

// 调用
Foo<string> fs;		// 实例化Foo<string>::Foo()
fs.Bar();			// 实例化Foo<string>::Bar()
Foo<int> fi;		// 实例化Foo<int>::Foo()
fi.Bar();			// // 实例化Foo<int>::Foo()