参考 C++模板第二版
1.模板的检查
模板检查两次。
第一次是在模板定义阶段会去语法检查,但是并不包含参数的检查。
第二次是在模板实例化的过程中,为确保模板正确,模板会再次检查。
2.函数模板的写法
(1)typename 标识了一个类型参数
(2)typename也可以采用class,二者等价的,但是typename除了class,还可以接受其他类。
#include<iostream>
#include<string>
// using namespace std;
/*typename 标识了一个类型参数
typename也可以采用class,二者等价的,但是typename除了class,还可以接受其他类。
这里的T必须支持小于运算符,
因为a和b的比较时候得使用到它
此外T支持返回值,所以T必须是可拷贝的。
*/
template<typename T>
T max (T a,T b)
{
return b < a ? a: b;
//返回a和b中的较大值
}
// {
// template<class T>
// T max (T a,T b)
// {
// return b < a ? a: b;
// //返回a和b中的较大值
// }
// }
int main(){
int i = 42;
/*注意在调用 max()模板的时候使用了作用域限制符::。这样程序将会在全局作用域中查找
max()模板。否则的话,在某些情况下标准库中的 std::max()模板将会被调用,或者有时候不
太容易确定具体哪一个模板会被调用*/
std::cout<< "max(7,i)"<<::max(7,i)<<std::endl;
double f1 = 3.4;
double f2 = -6.7;
std::cout<<"max(f1,f2)"<<::max(f1,f2)<<std::endl;
std::string s1 = "mathe";
std::string s2 = "math";
std::cout<<"max(s1,s2)"<<::max(s1,s2)<<std::endl;
return 0;
}
// max(7,i)42
// max(f1,f2)3.4
// max(s1,s2)mathe
3.返回类型的模板参数推导
可以采用auto关键字
(1)auto max (T1 a,T2 b) ->typename std::decay<decltype(truea:b)>::type 是一种类型萃取的方法,可以获得返回类型的decay值,所谓的decay值就是去除引用以及const等特性,获得一个被削弱的返回值。
(2)auto本身返回的结果也是decay的。
#include<iostream>
#include<string>
#include<type_traits>
template<typename T1,typename T2>
auto max (T1 a,T2 b) ->typename std::decay<decltype(true?a:b)>::type
{
return b < a ? a: b;
//返回a和b中的较大值
}
//->typename std::decay<decltype(true?a:b)>::type
//返回对应的类型,并且用decay去除引用等特性,返回其type为目标类型,再用typename获得结果
//这样获得的结果将会是退化之后的结果
//auto本身获得的结果就是退化的结果
// int i = 42;
// int const& ir = i; // ir 是 i 的引用
// auto a = ir; // a 的类型是 it decay 之后的类型,也就是 int
int main(){
std::cout<< "max(7,2.5)"<<::max(7,2.5)<<std::endl;
std::cout<< "max(7.1,2.5)"<<::max(7.1,2.5)<<std::endl;
return 0;
}
(3)也可以采用std::common_type_t<T1,T2>,它也是退化的,会在返回结果中选择合适的值来返回。
#include<iostream>
#include<string>
#include<type_traits>
template<typename T1,typename T2>
std::common_type_t<T1,T2> max (T1 a,T2 b)
{
return b < a ? a: b;
//返回a和b中的较大值
}
//std::common_type_t<T1,T2> c++14
//等价于 typename std::common_type<T1,T2>::type c++11
//它的结果也是退化的
int main(){
std::cout<< "max(7,2.5)"<<::max(7,2.5)<<std::endl;
std::cout<< "max(7.1,2.5)"<<::max(7.1,2.5)<<std::endl;
return 0;
}
4.默认模板参数
直接将返回指的默认类型在template中去定义
#include<iostream>
#include<string>
#include<type_traits>
template<typename T1,typename T2,typename T3 = std::decay_t<decltype(true?T1():T2())>>
T3 max (T1 a,T2 b)
{
return b < a ? a: b;
//返回a和b中的较大值
}
//默认模板参数
//std::decay_t<decltype(true?T1():T2())
//typename T3 = std::common_type<T1,T2>>
int main(){
std::cout<< "max(7,2.5)"<<::max(7,2.5)<<std::endl;
std::cout<< "max(7.1,2.5)"<<::max(7.1,2.5)<<std::endl;
return 0;
}
5.重载
(1)模块解析会优先选择非模板的函数,
(2)如果可以实例化更匹配的函数,会选择模板函数
比如char可以转换为int,但是直接使用模板不用进行这种转换,所以会更加适合一些
(3)可以显示指示一个空的模板,它也会优先选择模板对象。
(4)模板参数推导不能自动类型转换,但是如果用非模板函数可以进行自动类型转换,那么选择非模板的函数来使用的。
#include<iostream>
#include<string>
#include<type_traits>
int max(int a,int b){
std::cout<<"this is int"<<std::endl;
return b<a?a:b;
}
template<typename T>
T max(T a, T b){
std::cout<<"this is template"<<std::endl;
return b<a ? a:b;
}
int main(){
//模块解析会优先选择非模板的函数,
//如果可以实例化更匹配的函数,会选择模板函数
//比如char可以转换为int,但是直接使用模板不用进行这种转换,所以会更加适合一些。
std::cout<< "max(7,42)"<<::max(7,42)<<std::endl;//this is int
std::cout<< "max(7.0,42.0)"<<::max(7.0,42.0)<<std::endl;//this is template
std::cout<< "max('a','b')"<<::max('a','b')<<std::endl;//this is template
//可以显示指示一个空的模板,它也会优先选择模板对象。
std::cout<< "max<>"<<::max<>(7,42)<<std::endl;//this is template
std::cout<< "max<double>"<<::max<double>(7,42)<<std::endl;//this is template
std::cout<< "max(11,'a')"<<::max(11,'a')<<std::endl; //this is int
//模板参数推导不能自动类型转换,但是这里可以用int max来作为函数使用,所以选择非模板参数来使用的。
return 0;
}
(5)当重载函数模板的时候,你要保证对任意一个调用,都只会有一个模板匹配。如果两个模板都是匹配的, 这会导致模板解析过程不知道该调用哪一个模板, 从而导致未知错误。因此当重载函数模板的时候,你要保证对任意一个调用,都只会有一个模板匹配
#include <iostream>
#include <string>
#include <type_traits>
template <typename T1, typename T2>
auto max(T1 a, T2 b)
{
std::cout<<"this is first"<<std::endl;
return b < a ? a : b;
}
template <typename RT, typename T1, typename T2>
RT max(T1 a, T2 b)
{
std::cout<<"this is second"<<std::endl;
return b < a ? a : b;
}
int main()
{
auto a = ::max(4, 7.2); // uses first template
auto b = ::max<long double>(7.2, 4); // uses second template
//当重载函数模板的时候,你要保证对任意一个调用,都只会有一个模板匹配
//auto c = ::max<int>(4, 7.2); //call of overloaded 'max<int>(int, double)' is ambiguous
// ERROR: both function templates match
// 两个模板都是匹配的, 这会导致模板解析过程不知道该调用哪一个模板, 从而导致未知错误。
// 因此当重载函数模板的时候,你要保证对任意一个调用,都只会有一个模板匹配
return 0;
}