typename 消除依赖名称的歧义
template<class Container>
void Print(const Container& v)
{
typename Container::const_iterator it = v.begin();
//Container::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
这段代码中,被注释掉的一行是会报错的,原因在于:在 C++ 模板中,当出现依赖于模板参数的嵌套名称时,编译器默认会假设它是一个静态成员变量或函数,而不是一个类型
Container::const_iterator 依赖于模板参数 Container, 在模板实例化之前,编译器不知道 Container 具体是什么类型,也就无法确定 const_iterator 是类型、静态成员还是成员函数 若不加typename编译器会将其默认解析为非类型(静态成员变量),这段代码本质就和 int a b = 5;一样毫无意义
typename用于显示声明 const_iterator 是 Container 内部定义的一个类型
非类型模板参数
//静态栈
#define N 10
template<class T>
class Stack
{
private:
T _a[N];
int _top;
};
void test1()
{
Stack<int> st1;//10个空间
Stack<int> st2;//100个空间
//无法解决
}
当我们定义一个静态栈时,无法解决不同栈的容量问题
这时候就需要用到非类型模板参数
template<class T,size_t N>//非类型模板参数
class Stack
{
public:
/*void func()
{
//常量,无法修改
//必须是整型
N = 0;
}*/
private:
T _a[N];
int _top;
};
void test1()
{
Stack<int,10> st1;//10个空间
Stack<int,100> st2;//100个空间
}
非类型模板参数:1.常量,无法修改 2.必须是整型
模板的特化
函数模板的特化
template<class T>
bool Less(T left, T right)
{
return left < right;
}
//针对 int* 的全特化
template<>
bool Less<int*>(int* left, int* right)
{
return *left < *right; // 解引用后比较值
}
//函数更常用函数重载
template<class T>
bool Less(T* left, T* right)
{
return *left < *right;
}
void test2()
{
cout << Less(1, 2) << endl;
int a = 2, b = 1;
cout << Less(&a, &b) << endl;
}
在上述代码中,仅靠第一个Less函数模板时,在进行cout << Less(&a, &b) << endl;时,比较两个地址的大小毫无意义,我们真正想要的是当模板参数为指针类型时,比较指针所指对象的值,而不是地址
为此C++提供两种解决方案:函数模板全特化,函数重载
特化为函数单独提供一个int*的版本以适应使用需求,但C++中函数模板仅支持全特化,不支持偏特化;
而更为常用的是函数重载:
-
当传入普通类型(如
int)时,两个模板都参与重载决议,但Less(T*, T*)不匹配,最终选用通用模板Less(T, T)。 -
当传入指针类型(如
int*)时,Less(T*, T*)比Less(T, T)更特化(因为前者只能匹配指针,后者能匹配任意类型),根据“最特化匹配”原则,编译器会优先选用指针重载版本。
类模板特化
template<class T1, class T2>
class Data
{
public:
Data()
{
cout << "Data<T1, T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//全特化
template<>
class Data<int, double>
{
public:
Data()
{
cout << "Data<int, double>" << endl;
}
private:
int _d1;
double _d2;
};
//偏特化
//1.固定参数
template<class T1>
class Data<T1, double>
{
public:
Data()
{
cout << "Data<T1, double>" << endl;
}
};
//2.限制参数类型
template<class T1, class T2 >
class Data<T1*,T2*>
{
public:
Data()
{
cout << "Data<T1*, T2*>" << endl;
}
};
void test3()
{
Data<int, int> d1;
Data<int, double> d2;
Data<char, double> d3;
Data<char*, void*> d4;
}
全特化类是一个完全独立的类,你可以自由重新设计其内部实现,甚至改变对外接口。
偏特化允许只固定部分参数或对参数施加类型约束
模板的分离编译
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
这样会导致链接错误
解决方案:
1.直接将定义放入头文件(最常用)
// a.h
template<class T>
T Add(const T& left, const T& right)
{
return left + right; // 定义写在头文件里
}
2.使用export(已废弃)
3.显式实例化
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// 显式实例化:强制编译器在 a.cpp 中生成 int 和 double 版本的二进制代码
template int Add<int>(const int&, const int&);
template double Add<double>(const double&, const double&);

957

被折叠的 条评论
为什么被折叠?



