浅谈 C++ 模板语法

· · 科技·工程

0. 日志

[2025/8/4] 第一版正式开始编辑!\ [2025/8/13] 第二版,补充模板进阶技巧与附录,突破 {3k} 字!\ [2025/8/15] 第三版,补充模板进阶技巧与附录,突破 {4k} 字!

1. 模板基础

知周所众,模板(Template) 是用来简化各种函数重载的利器。当需要编写功能相同但类型不同的代码时,模板能避免重复牛马劳动。

1.1 函数模板

一般模板的语法是这样的:

template<typename 模板类型名,typename 模板类型名,...>
<返回类型> <函数名>(<参数列表>)
{
    <具体实现>
}

当然,上述代码中的 typename 也可以替换成 class。不过建议使用 typename 来向后兼容 C++ 版本。注意一点:一个模板定义头只能用于一个函数 / 类。 那么上面的 max 函数就可以编写成:

template<typename Type>
Type max(Type a,Type b){return a>b?a:b}

不需要为不同类型写多个相似函数:

template<typename T>
T UserDefineMax(T a,T b)
{
    return a>b?a:b;
}
cout<<UserDefineMax(3,5);     //T=int
cout<<UserDefineMax(3.14,2.0);//T=double

1.2 类模板

考虑到有些程序需要设计一些不同类型但是实现基本一样的类,于是就有了“模板类”。

template<typename 模板类型名,typename 模板类型名,...>
class <类名>
{
    <类的成员>
};

注意,同函数模板一样,每一个模板定义头都只能对应一个函数 / 类。\ 创建通用容器 / 工具类:

template<typename T>
class UserDefineVector
{
    private:
        T*Data;
        int Size;
    public:
        UserDefineVector()
        {
            Data=nullptr;
            Size=0;
        }
        void PushBack(T value){/*...*/}
};
UserDefineVector<int>IntVec;
UserDefineVector<string>StrVec;

2.模板进阶

自见,已经不想打字了(下面自己看实例)。

2.1 非类型参数

模板不仅能参数化类型,还能参数化

template<typename T,int Size>
class FixedArray
{
private:
    T Arr[Size];
public:
    T&operator[](int index)
    {
        return Arr[index];
    }
};
FixedArray<double,100>SensorData;

2.2 默认模板参数

像函数默认参数一样方便:

template<typename T=int>//默认int类型
class DataLogger
{
public:
    void Log(T data){/*...*/}
};
DataLogger<>DefaultLogger;//自动用int
DataLogger<string>TextLogger;

2.3 模板特化

对特定类型定制实现:

//通用模板
template<typename T>
struct IsPointer{static const bool value=false;};
//针对指针类型的特化
template<typename T>
struct IsPointer<T*>{static const bool value=true;};
cout<<IsPointer<int>::value;   //0
cout<<IsPointer<int*>::value;  //1

2.4 可变参数模板(C++11)

在 C++11 有了参数安全的变长参数列表函数,此时就可以用像传统的 printf 函数一样,拥有可变参数模板。\ 这个是变长参数函数的模板:

template<typename <类型替代名>>
<返回类型><函数名>(<第一对参数><参数包>...);
template<typename <类型替代名>>
<返回类型><函数名>(<一对参数>);

举个栗子:编写一个可以容纳很多参数的 max 函数。\ 首先编写 {2} 个模板函数:

template<typename Tp>
Tp max(Tp n){return n};
template<typename Tp,typename ...Targs>
Tp max(Tp n,Targs... Other){return n>max(Other...)?n:max(Other...)}

我们只看第二个模板函数:\ (1)模板和函数原型中使用了 {2} 个省略号,这是参数包符号,表示回传很多参数。\ (2)我们使用递归:每次都与后面的最大数去比(尽管效率很低下,因为不是记忆化递归)。\ (3)注意:lst... 是表示整个参数包

3. 附录

3.1 typename 的双重身份

template<typename T>
void UserDefineFunction()
{
    typename T::Iterator it;//必须加typename!
}

template<> structFactorial<0> { static const intvalue=1; }; int x=Factorial<5>::value;//120,这个值在编译时就被展开后计算

### 3.4 概念约束(C++20)
给模板参数加条件:
```cpp line-numbers
template<typenameT>conceptAddable=requires(Ta,Tb)
{
    {a+b}->convertible_to<T>;//要求支持operator +(...)运算
};

template<AddableT>//约束模板参数
TSum(Ta,Tb){return a+b;}

Sum(3,5);//合法
Sum("a","b");//编译错误:不满足Addable

4. 结语(广告)

模板是 C++ 泛型编程的基石(像废话),从 STL 容器到智能指针都依赖它。掌握模板,你就能造出更强大的轮子(牛马)!\ 广告位:浅谈C++类语法\ 非常感谢各位大佬看到这里。鄙人不才,才疏学浅。如有错误请私信指出。非常感谢您为我(们)的提出宝贵建议。也希望您能将这篇文章推荐给其他 OIer 阅读并轰炸我的私信。(复制粘贴)