template—模板

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&);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehelm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值