函数模板
编译器在使用模板前要经过 模板参数推演–函数模板–推演参数实例化
函数模板的定义通常以关键字template开始,后面跟着模板参数列表和函数定义。模板参数列表中可以包含类型参数、非类型参数和模板参数包等。
#include<iostream>
using namespace std;
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
//泛型编程 模板
template<typename T> //模板参数(模板类型)---类似函数参数(参数对象) T为类型名字(自取)
// 也可以使用 template<class T> 极少数场景会有区别
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
template<class T>
T* Func(int n)
{
T* a = new T[n];
return a;
}
template<typename T1,typename T2>
T1 Add(const T1& left, const T2& right)
{
return left + right;
}
//参考 https://cplusplus.com/reference/
int main()
{
/*A* p1 = new A;
A* p2 = (A*)malloc(sizeof(A));
if (p2 == nullptr)
{
perror("malloc fail");
}
new(p2)A(10);*/
int i = 0, j = 1;
double x = 1.1, y = 2.2;
char m = 'A', n = 'B';
Swap(i, j);
swap(x, y);
swap(m, n); //std空间自带swap函数
cout << i << j << x << y << m << n << endl;
Add(1, 2);
//Add(1.1, 1); //会在推演实例化报错 T类型不明确
//解决 自转 编译器自助推演 隐式实例化
Add((int)1.1, 1);
Add(1.1, (double)1);
//显式实例化
Add<int>(1.1, 1); //指定T的类型
Func<A>(10); //无法自动推演 必须显式实例化
Add(1, 1.1); //调用第二个双T的
Add(1.1, 1);
return 0;
}
实际是生成了不同的实例化函数
模板和auto的区别
在C++中,模板中的T和auto都是用于类型推导的机制,但是它们有一些区别。
-
T是模板参数,需要在模板定义时显式指定或者通过参数推导来确定具体的类型。T可以用于函数模板、类模板等的定义中,可以作为函数参数、类成员、局部变量的类型。T可以在函数体内部使用,可以进行模板特化和模板元编程等高级用法。
-
auto是C++11引入的关键字,用于自动推导变量的类型。auto可以用于函数返回值、函数参数、局部变量的类型推导。auto只能在函数体内部使用,不能进行模板特化和模板元编程等高级用法。
-
T可以用于多个参数的类型推导,可以推导出多个参数的类型,例如函数模板中的两个参数类型可以相同或不同。
auto只能用于单个变量的类型推导,无法推导出多个参数的类型。 -
T可以在函数模板中作为参数类型,可以进行模板参数的运算和类型转换等操作。
auto只能推导出变量的具体类型,无法进行类型转换等操作。
总的来说,T是一种通用的类型参数,可以在模板定义时指定或者通过参数推导来确定具体的类型,具有更大的灵活性和扩展性;而auto是一种用于自动推导变量类型的关键字,只能在函数体内部使用,用于简化代码和提高可读性。
类模板
类模板是一种通用的类定义,可以根据不同的类型参数生成不同的类。类模板的定义使用关键字template和模板参数来指定通用的类型,然后在类内部使用这些类型参数来定义成员变量、成员函数等。
#include<iostream>
using namespace std;
//typedef int STDataType;
template<typename T>
class Stack
{
public:
Stack(size_t capacity = 4)
:_a(nullptr)
, _top(0)
, _capacity(0)
{
if (capacity>0)
{
_a = new T[capacity];
_top = 0;
_capacity = capacity;
}
}
~Stack()
{
delete[] _a;
_a = nullptr;
_capacity = _top = 0;
}
void Push(const T& x);
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
const T& Top()
{
assert(_top > 0);
return _a[_top - 1];
}
private:
//STDataType* _a;
T* _a;
size_t _top;
size_t _capacity;
};
//类模板不支持声明和定义分离(分到.c和.h) 在一个文件中支持声明定义分离
template<class T>
void Stack<T>::Push(const T& x)
{
if (_top == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
T* tmp = new T[newcapacity];
if (_a)
{
memcpy(tmp, _a, sizeof(T) * _top);
delete[] _a;
}
_a = tmp;
_capacity = newCapacity;
}
_a[_top] = x;
++_top;
}
int main()
{
Stack<int> st1;
Stack<char> st2;//实际是两个类
st1.Push(1);
st1.Push(2);
st1.Push(3);
while (!(st1.Empty()))
{
cout << st1.Top() << ' ';
st1.Pop();
}
cout << endl;
return 0;
}
模板的显示实例化
在C++中,可以使用显示实例化来显式地告诉编译器实例化一个特定的模板。显示实例化可以用于函数模板和类模板。
对于函数模板,可以使用以下语法进行显示实例化:
template <typename T>
void foo(T arg);
template void foo<int>(int arg); // 显示实例化为int类型
template void foo<double>(double arg); // 显示实例化为double类型
在上面的代码中,template void foo<int>(int arg);
和template void foo<double>(double arg);
分别表示将foo
函数模板实例化为int
类型和double
类型。
对于类模板,可以使用以下语法进行显示实例化:
template <typename T>
class Bar {
// 类定义
};
template class Bar<int>; // 显示实例化为int类型
template class Bar<double>; // 显示实例化为double类型
在上面的代码中,template class Bar<int>;
和template class Bar<double>;
分别表示将Bar
类模板实例化为int
类型和double
类型。
显示实例化可以在编译时生成特定类型的函数或类的实例,可以用于优化编译时间和生成更小的可执行文件。但需要注意的是,显示实例化会增加编译时间和生成的代码量,因此应该谨慎使用,只在必要时进行显示实例化。
另外,C++17引入了template<>
的语法,可以直接对模板进行特化,而无需提供具体的参数类型。这种特化方式也可以用于显示实例化,例如:
template <>
void foo<int>(int arg); // 显示实例化为int类型的特化