基本概念
- 类模板中的成员函数,只有源程序代码中出现调用这些成员函数的代码时,这些成员函数才会出现在一个实例化了的类模板中。
- 类模板中的成员函数模板,只有源程序中出现调用这些成员函数模板的代码时,这些成员函数的具体实例才会出现在一个实例化了的类模板中。
- 目前编译器并不支持虚成员函数模板:因为虚函数表vtbl的大小是固定的。C++之父说法:如果允许虚函数模板时,则每次有人用新的参数类型调用该虚函数时,就必须给对应的虚函数表再增加一项,这意味着只有链接程序才能去构造虚函数表,(在编译期间虚函数表vtbl就不能够固定),并在表中设置有关函数,因此,成员函数模板绝不能是虚的。
- 类模板中可以有普通的虚成员函数(虚函数),这并没有什么问题。大家都知道,普通成员函数如果不被调用的情况下,是不会被实例化出来的。但是,对于虚函数来说,不管是否调用,编译器都会把它实例化出来,因为编译器要创建虚函数表vtbl,该表中的每个具体表项都对应一个具体的虚函数地址,所以编译器必须的把所有的虚函数都实例化出来。
- 拷贝构造函数模板不是拷贝构造函数,拷贝赋值运算符模板不是拷贝赋值运算符函数,同时拷贝构造函数模板也不是拷贝构造函数。因为拷贝构造函数或者拷贝赋值运算符要求拷贝的对象类型完全相同,而拷贝构造函数和拷贝赋值运算符模板没有这种要求。
- 拷贝构造模板绝对永远不可能成为拷贝构造函数。编译器不会用调用拷贝构造函数模板来代替调用拷贝构造函数。
- 拷贝构造函数模板什么时候调用?类型不同(都是用类模板A实例化出来的,例如:A,A) 的两个对象,用一个拷贝另外一个时候。
- 拷贝赋值运算符模板 永远不可能成为拷贝赋值运算符函数,调用行为和拷贝构造函数模板行为是一样的。
成员函数模板特化
- 成员函数模板特化,不同的编译器支持程度不一样。
- 类模板中成员函数全特化还不太完善,写代码时需要注意测试。
- 实际工作中,建议把这些特化版本写在模板内部,然后类模板写在头文件中。
类模板中的类模板(嵌套)
- 根据类作用域符号 :: 来界定。
变量模板与成员变量模板
变量模板的特化(Variable templates)
- C++14 新标准中引入的。(一般放在 .h头文件中)
- 三种零初始化方式。a) int temp=int() b) int temp2={} c) int temp3 = int{}
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp1
{
template <typename T>
T g_myvar{};
}
int main(int argc,char* argv[])
{
_nmsp1::g_myvar<float> = 15.6f;
_nmsp1::g_myvar<int> = 13;
cout<<_nmsp1::g_myvar<float> <<endl;
cout<<_nmsp1::g_myvar<int> <<endl;
return 0;
}
- 变量模板的全特化。变量模板全特化的时候,并不需要正在特化的类型(double) 与 这个变量模板的类型(char) 保持一致。
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp1
{
template <typename T>
T g_myvar{};
template<>
char g_myvar<double>{};
}
int main(int argc,char* argv[])
{
_nmsp1::g_myvar<double> = '2';
cout<<_nmsp1::g_myvar<double> <<endl;
printf("%d\n",_nmsp1::g_myvar<double>);
printf("%c\n",_nmsp1::g_myvar<double>);
return 0;
}
- 变量模板的偏特化。
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp1
{
template <typename T>
T g_myvar{};
template<>
char g_myvar<double>{};
template <typename T>
T g_myvar<T*> {120};
}
int main(int argc,char* argv[])
{
_nmsp1::g_myvar<double> = '2';
cout<<_nmsp1::g_myvar<double> <<endl;
printf("%d\n",_nmsp1::g_myvar<double>);
printf("%c\n",_nmsp1::g_myvar<double>);
cout<<_nmsp1::g_myvar<int*> <<endl;
cout<<_nmsp1::g_myvar<int> <<endl;
return 0;
}
默认模板参数
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp2
{
template <typename T=int>
T g_myvar{};
}
int main(int argc,char* argv[])
{
_nmsp2::g_myvar<int> = 13;
cout<<_nmsp2::g_myvar<> <<endl;
_nmsp2::g_myvar<> = 26;
cout<<_nmsp2::g_myvar<> <<endl;
return 0;
}
非类型模板参数
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp3
{
template <typename T,int value>
T g_myvar[value];
}
int main(int argc,char* argv[])
{
for(size_t i=0;i<15;i++)
{
_nmsp3::g_myvar<int,15>[i] = i+1;
}
for(size_t i=0;i<15;i++)
{
cout<<_nmsp3::g_myvar<int,15>[i] <<endl;
}
return 0;
}
变量模板的另一种形式
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp4
{
template <typename T>
struct B
{
public:
const static T value = {160};
};
template <typename T>
int g_myvar4 = B<T>::value;
}
int main(int argc,char* argv[])
{
cout<< _nmsp4::g_myvar4<int> <<endl;
_nmsp4::g_myvar4<int> =152;
cout<< _nmsp4::g_myvar4<int> <<endl;
cout<<_nmsp4::B<int>::value <<endl;
return 0;
}
成员变量模板
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp5
{
template <typename T>
class Demo
{
public:
template<typename W>
static W m_tpi;
};
template <typename T>
template<typename W>
W Demo<T>::m_tpi = 5;
}
int main(int argc,char* argv[])
{
cout<<_nmsp5::Demo<float>::m_tpi<int> <<endl;
_nmsp5::Demo<float>::m_tpi<int> = 150;
cout<<_nmsp5::Demo<float>::m_tpi<int> <<endl;
return 0;
}
别名模板与成员别名模板
- 别名模板(Alias Templates),C++11 新标准引入的,引入的目的不但可以简化书写,而且可以达到一些通过其他手段难以达到的目的。
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
namespace _nmsp6
{
template <typename T>
using str_map_t = std::map<std::string, T>;
template <typename W>
class E
{
private:
template <typename W>
using str_map_t = std::map<std::string, W>;
public:
str_map_t<int> map1;
};
}
int main(int argc, char* argv[])
{
_nmsp6::str_map_t<int> map1;
map1.insert({ "first",1 });
map1.insert(make_pair("second", 2));
for (auto iter = map1.begin(); iter != map1.end(); iter++)
{
cout << iter->first << "," << iter->second << endl;
}
cout << endl;
_nmsp6::E<float> obja;
obja.map1.insert({"three",3});
obja.map1.insert(make_pair("four",4));
for (auto iter = obja.map1.begin(); iter != obja.map1.end(); iter++)
{
cout << iter->first << "," << iter->second << endl;
}
return 0;
}