C++ 模板初阶 —— 让编译器帮你写“同一套逻辑、不同类型“的代码

Java服务端实现Google Pay支付功能 全网最全代码、最清晰服务端处理Google Pay,最清晰讲解Google Pay集成逻辑。 阅读详情

C++ 模板初阶 —— 让编译器帮你写"同一套逻辑、不同类型"的代码

适合读者:学过函数重载和类与对象,正在接触 C++ 模板的同学。
本文目标:回答一个问题 —— 为什么需要模板?函数模板和类模板分别怎么用、底层干了什么?用的时候最容易踩哪些坑?
可信度声明:文中代码基于 g++ -std=c++17 编译验证,报错信息均为真实编译输出。


1. 一句话总结

模板 = 给编译器一个"模子",让编译器根据你传入的类型自动"印"出对应的函数或类。 函数重载是"手写 N 份,编译器帮你选";模板是"写 1 份,编译器帮你生成 N 份"。


2. 知识地图

模板初阶
  ├── 泛型编程(动机:写与类型无关的通用代码)
  │     └── 函数重载的痛点:代码重复、维护困难
  │
  ├── 函数模板
  │     ├── 格式:template<typename T> + 函数定义
  │     ├── 原理:编译期推演 + 生成(不是运行时!)
  │     ├── 实例化
  │     │     ├── 隐式实例化:编译器根据实参推演 T
  │     │     └── 显式实例化:用户在 <> 中指定 T
  │     └── 匹配原则:非模板函数 vs 模板函数的优先级
  │
  └── 类模板
        ├── 格式:template<typename T> + 类定义
        ├── 类外定义成员函数:必须补 template 头 + 类名写全 <T>
        ├── 实例化:类名<T> 才是类型,类名不是
        └── 声明定义分离问题:模板不能拆到 .h / .cpp(链接错误)

关系说明:泛型编程是"为什么需要模板"的动机层;函数模板和类模板是"怎么用"的两个语法层。两者共享 template<typename T> 这套语法,但实例化方式不同(函数可以隐式推演,类必须显式指定)。匹配原则是函数模板独有的细节——它解释了"同名非模板函数和模板函数同时存在时,编译器选谁"。


3. 核心概念深度解析

3.1 泛型编程 —— 动机:函数重载不够用

先看一个最朴素的问题:写一个通用的 Swap 函数。

void Swap(int& left, int& right)
{
    int temp = left;
    left = right;
    right = temp;
}

void Swap(double& left, double& right)
{
    double temp = left;
    left = right;
    right = temp;
}

void Swap(char& left, char& right)
{
    char temp = left;
    left = right;
    right = temp;
}
// ...... 每来一个新类型就抄一遍

函数重载能编译通过,但有两个硬伤:

痛点说明
代码复用率低重载函数的函数体几乎一模一样,仅类型不同;新类型出现就要加一份
可维护性差逻辑一改,所有重载版本都要同步改;漏改一个就是 bug

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

思路转换:不要自己写 N 份,而是给编译器一个"模子",让它根据类型帮你印出来。


3.2 函数模板

3.2.1 概念与格式

函数模板代表了一个函数家族,与类型无关,使用时被参数化,根据实参类型产生函数的特定类型版本。

template<typename T>
void Swap(T& left, T& right)
{
    T temp = left;
    left = right;
    right = temp;
}

语法拆解:

template<typename T>       ← 模板参数声明("告诉编译器 T 是个类型占位符")
       ↑         ↑
   关键字    模板参数名(可以用 class 代替 typename,但不能用 struct)

多模板参数:当两个参数类型不同时,需要两个模板参数:

template<typename T1, typename T2>
T2 Add(const T1& a, const T2& b)
{
    return a + b;
}

⚠️ typename 和 class 在模板参数列表中完全等价,但不能用 struct 代替。习惯上用 typename 更语义化(“这是一个类型名”),但很多老代码和标准库用 class。

3.2.2 原理:编译期推演,不是运行时

函数模板本身不是函数——它是一个蓝图/模具。在编译阶段,编译器根据你传入的实参类型,推演出 T 的具体类型,然后生成一份该类型的函数代码。

Swap(i, j);      // 编译器推演 T = int,生成 Swap(int&, int&)
Swap(x, y);      // 编译器推演 T = double,生成 Swap(double&, double&)

     源码层面                     编译后(二进制里)
  ┌──────────────┐           ┌──────────────┐
  │ template<T>  │           │ Swap(int&,   │  ← 一份独立的函数
  │ void Swap()  │    ──→    │      int&)   │
  └──────────────┘           │ Swap(double&,│  ← 另一份独立的函数
                             │      double&)│
                             └──────────────┘

关键理解:模板就是将本来应该我们做的重复的事交给了编译器。代价是 Swap<int> 和 Swap<double> 在二进制里是两份独立的代码——代码膨胀是模板的固有代价。

3.2.3 实例化:隐式 vs 显式

用不同类型的参数使用函数模板时,称为函数模板的实例化。分两种:

① 隐式实例化:让编译器推演

template<typename T>
T Add(const T& a, const T& b)
{
    return a + b;
}

int i = 1, j = 2;
double x = 2.2, y = 3.3;

Add(i, j);      // T 推演为 int  ✅
Add(x, y);      // T 推演为 double  ✅
Add(i, x);      // ❌ 编译报错!

Add(i, x) 为什么报错? 编译器从 i 推演 T = int,从 x 推演 T = double,一个模板参数 T 推出了两个不同类型 → 矛盾,报错。

⚠️ 在模板中,编译器一般不会进行类型转换。因为一旦转换出问题,编译器就得"背黑锅"——它不敢擅自做主。

两种修法:

// 方法一:用户自己强制转换(推导实例化)
Add(i, (int)x);         // 两个参数都是 int,T 推演为 int
Add((double)i, x);      // 两个参数都是 double,T 推演为 double

// 方法二:显式实例化——在函数名后的 <> 中指定 T 的类型
Add<double>(i, x);      // T = double,i 会隐式转换为 double
Add<int>(i, x);         // T = int,x 会截断为 int

② 显式实例化:用户指定类型

// 显式实例化格式:函数名<类型>(参数)
Add<int>(1, 2.0);       // T = int,2.0 隐式截断为 2
Add<double>(1, 2.0);    // T = double,1 隐式提升为 1.0
Add<int, double>(1, 2.0); // 两个模板参数分别指定

如果类型不匹配,显式实例化时编译器会尝试隐式类型转换;转换失败才报错。

什么时候必须显式实例化? 当编译器无法从实参推演出 T 时:

template<typename T>
T* func1(int n)
{
    return new T[n];   // 动态分配 n 个 T 类型的空间
}

// func1(10);           // ❌ 编译器推不出 T 是什么
double* p1 = func1<double>(10);   // ✅ 必须显式指定 T = double
delete[] p1;

函数参数里只有 int n,没有任何 T 类型的参数 → 编译器无从推演 → 必须显式实例化。

3.2.4 模板参数的匹配原则

当非模板函数和同名函数模板同时存在时,编译器怎么选?三条规则:

规则一:非模板函数和模板函数可以共存

// 专门处理 int 的加法函数
int Add(int left, int right)
{
    return left + right;
}

// 通用加法函数模板
template<typename T>
T Add(T left, T right)
{
    return left + right;
}

Add(1, 2);          // 调用非模板版本(完全匹配,不需要实例化)
Add<int>(1, 2);     // 调用模板实例化版本(显式要求)

规则二:条件相同时,优先调用非模板函数;但模板能产生更好匹配时,选模板

int Add(int left, int right)
{
    return left + right;
}

template<typename T1, typename T2>
T1 Add(T1 left, T2 right)
{
    return left + right;
}

Add(1, 2);          // 非模板完全匹配 → 调非模板
Add(1, 2.0);        // 模板能生成 Add(int, double),更匹配 → 调模板

规则三:模板函数不允许自动类型转换,普通函数可以

int Add(int left, int right) { return left + right; }
template<typename T>
T Add(T left, T right) { return left + right; }

// 如果只有模板版本:Add(1, 2.0) → 推演矛盾 → 报错
// 如果有非模板版本:Add(1, 2.0) → 2.0 截断为 2 → 调非模板 → 返回 3

三条规则总结成一句话:编译器优先选"不需要额外加工就能匹配"的版本;非模板函数能自动类型转换,模板不能——这决定了选择顺序。


3.3 类模板

3.3.1 为什么需要类模板

和函数同样的动机:Stack<int>、Stack<double>、Stack<string> 的逻辑完全一样,只是元素类型不同。

// 没有模板:手写 N 份
class StackInt    { int*    _array; /* ... */ };
class StackDouble { double* _array; /* ... */ };
class StackString { string* _array; /* ... */ };
// 改一个 bug 要改 N 个文件 💀

类模板 = 把"类型"本身变成参数:

template<typename T>
class Stack
{
public:
    Stack(int capacity = 10);
    void Push(const T& data);
    // ...
private:
    T* _array;        // 元素类型由 T 决定
    int _size;
    int _capacity;
};

Stack<int>    st1;    // T = int
Stack<double> st2;    // T = double
3.3.2 语法要点

① 类内定义成员函数:T 直接当类型用

template<typename T>
class Stack
{
public:
    Stack(int capacity = 10)
        : _array(new T[capacity])
        , _size(0)
        , _capacity(capacity)
    {}
    // ...
};

② 类外定义成员函数:必须补 template 头,类名写全 Stack<T>

// 类外定义 Push:每个函数前都要补上模板声明
template<typename T>
void Stack<T>::Push(const T& data)
{
    _array[_size] = data;
    ++_size;
}

⚠️ Stack 是类名,Stack<T> 才是类型。类外写 Stack::Push 会编译报错——编译器不知道你要哪个 T 的 Push。

③ 模板不能声明和定义分离到 .h / .cpp

这是模板与普通类最大的工程差异之一:

普通类:  声明放 .h,定义放 .cpp  ✅
模板类:  声明放 .h,定义放 .cpp  ❌ 链接错误!

原因:模板的定义必须在实例化的翻译单元里可见。Stack<int> 的实例化发生在调用方(比如 main.cpp),而 Push 的定义在 Stack.cpp 里——main.cpp 看不到定义,无法实例化;Stack.cpp 里没有人用 Stack<int>,也不会实例化 → 链接时找不到 Stack<int>::Push 的符号 → 链接错误。

常见解决方案:

  • 全部写在头文件(最常见,标准库就这么干)
  • 显式实例化:在 .cpp 末尾写 template class Stack<int>;,强制编译器在此翻译单元生成 Stack<int> 的全部代码
3.3.3 类模板的实例化
Stack<int>    st1;    // 用 int 替换 T,生成一份完整的类
Stack<double> st2;    // 用 double 替换 T,生成另一份完整的类

类模板和函数模板实例化的区别:

函数模板类模板
实例化方式可以隐式推演(编译器根据实参推 T)必须显式指定(Stack<int>)
原因函数参数里有 T 类型的参数可供推演类的定义不涉及具体参数,T 没法推
示例Add(1, 2) → 自动推 T = intStack<int> st; → 手动写 <int>

Stack 是类名(模板的"蓝图"),Stack<int> 才是真正的类型。用 Stack 声明变量(不加 <...>)会编译报错。

3.3.4 类模板中的资源管理(三法则同样适用)

你的 Template.cpp 里写了一个 Stack<T>,它管理着堆内存(T* _array),所以三法则全套:

template<typename T>
class Stack
{
public:
    // 构造:申请资源
    Stack(int capacity = 10)
        : _array(capacity > 0 ? new T[capacity] : nullptr)
        , _size(0)
        , _capacity(capacity)
    {
        assert(capacity > 0);
    }

    // 析构:释放资源
    ~Stack()
    {
        delete[] _array;
    }

    // 深拷贝构造:申请自己的内存,逐元素复制
    Stack(const Stack& other)
        : _array(other._capacity > 0 ? new T[other._capacity] : nullptr)
        , _size(other._size)
        , _capacity(other._capacity)
    {
        for (int i = 0; i < other._size; i++)
        {
            _array[i] = other._array[i];
        }
    }

    // 拷贝赋值:copy-and-swap
    Stack& operator=(const Stack& other)
    {
        if (this != &other)
        {
            Stack tmp(other);
            std::swap(_array, tmp._array);
            std::swap(_size, tmp._size);
            std::swap(_capacity, tmp._capacity);
        }
        return *this;
    }

private:
    T*  _array;
    int _size;
    int _capacity;
};

模板 + 三法则 = “资源管理模板”。逻辑和 Stack<int> 完全一样,只是把 int 换成了 T。这就是模板的价值:写一次,对任意 T 生效。


4. 注释提炼

4.1 知识注解类 —— 扩展科普

“为什么加 const?”

你的代码注释写道:

必须加 const,因为如果后面的参数是一个常量引用类型的对象,防止在函数中修改传入的对象。加 const 之后,即使后面的参数是一个右值,也不会报错出 bug。

展开说明:

template<typename T>
T Add(const T& a, const T& b)   // const 引用
{
    return a + b;
}

Add(1, 2);     // 1 和 2 是右值(临时值)
// 如果不加 const:T& 不能绑定右值 → 编译报错
// 加了 const:const T& 可以绑定右值 → 合法

不加 const 的话,Add(1, 2) 这种写法直接编译不过。所以模板参数用 const T& 不只是"好习惯",是功能需求。

“typename 和 class 有什么区别?”

在模板参数列表 template<...> 中,typename 和 class 完全等价,可以互换。但 typename 还有一个额外用途——依赖类型的消歧(模板进阶内容):

template<typename T>
void func()
{
    // T::iterator 是一个类型还是一个静态变量?编译器不知道
    typename T::iterator it;   // typename 告诉编译器:这是个类型
}

所以养成用 typename 的习惯更好——它在模板进阶中还有用,而 class 在这里只是历史遗留。

4.2 个人理解类

我的理解:模板的本质是"延迟绑定类型"。

普通函数/类在写代码时类型就定了;模板在写代码时只画蓝图,类型在使用时才绑定。这和面向对象的多态(运行时绑定)是两条路——模板是编译期的,没有虚函数调用的运行时开销,代价是代码膨胀和编译变慢。

我的理解:函数模板的隐式实例化 = 编译器的"类型推导"。

Add(1, 2) 不需要写 Add<int>(1, 2),是因为编译器能从 1 和 2 推出 T = int。但这个推导有边界——推不出来就报错,不会擅自做类型转换。编译器宁可报错也不"猜",这是模板的安全设计。

我的理解:类模板的实例化是"编译期的 new"。

Stack<int> 就是在编译期"new"出了一个新类——编译器用 int 替换模板里所有的 T,生成一份完整的类定义。和运行时的 new 无关,但思想一样:给一个"模子",填入具体的东西,得到一个具体的产物。

4.3 踩坑感悟

踩坑 1:模板参数推演冲突 —— Add(i, x) 报错

这是新手最常遇到的模板编译错误。一个 T,两个不同类型的实参,编译器左右为难。

修法:① 强制转换实参 Add(i, (int)x);② 用两个模板参数 template<typename T1, typename T2>;③ 显式实例化 Add<double>(i, x)。

选择建议:如果两个参数逻辑上可以是不同类型,直接用双模板参数版本。

踩坑 2:类模板忘记写 <T>

类外定义成员函数时,void Stack::Push(...) 会报错——Stack 只是模板名,不是类型。必须写成 void Stack<T>::Push(...)。

踩坑 3:模板声明定义分离到 .h/.cpp → 链接错误

这是最阴险的坑:编译不报错,链接才报错。原因见 3.3.2。模板代码全部写头文件里是最简单的解法。

踩坑 4:func1 类模板参数推不出来

T* func1(int n) 的参数里没有 T,编译器推不出来。返回值不参与推演! 必须显式实例化 func1<double>(10)。

踩坑 5:类名 vs 类型混淆

Stack 是模板名(蓝图),Stack<int> 才是类型。声明变量、做参数、做返回值时必须写全 Stack<int>,不能只写 Stack。

4.4 对比总结类

维度函数重载函数模板类模板
写法每个类型手写一份写一份,编译器生成多份写一份,编译器生成多份
实例化无(每份都是独立函数)可以隐式推演或显式指定必须显式指定 <类型>
代码膨胀源码就膨胀编译后膨胀(每种类型一份)编译后膨胀(每种类型一份)
新类型支持加一个重载函数自动(编译器生成)自动(编译器生成)
维护成本高(改一处漏 N 处)低(改模板即可)低(改模板即可)
类型安全各版本独立编译期检查编译期检查

函数模板 vs 普通函数的匹配优先级:

场景编译器选择原因
非模板完全匹配非模板函数不需要实例化,优先选现成的
非模板需要转换,模板完全匹配模板函数模板能生成更匹配的版本
两者都完全匹配非模板函数同条件下优先非模板
模板推演矛盾报错模板不做隐式转换,非模板可以做

5. 代码修正

以下修正均基于你的 5.Template.cpp 中的问题。

修正 1:rturn 拼写错误

问题:第 28 行 rturn a + b;,应该是 return。

// 修改前
rturn a + b;

// 修改后
return a + b;

修正 2:拷贝构造函数的初始化列表引用了未初始化的 _capacity

问题:初始化列表里用了 _capacity > 0,但 _capacity 还没被初始化(它在列表末尾才赋值),读出的是随机值。

// 修改前
Stack(const Stack& other)
    : _array(_capacity > 0 ? new T[_capacity] : nullptr)   // ← _capacity 还没初始化!
    , _size(0)
    , _capacity(0)

// 修改后:应该用 other._capacity
Stack(const Stack& other)
    : _array(other._capacity > 0 ? new T[other._capacity] : nullptr)
    , _size(other._size)
    , _capacity(other._capacity)

这和"初始化列表顺序 = 声明顺序"是同一类坑。即使声明顺序对了,也不能在初始化列表里"用后面的成员来初始化前面的成员"——因为赋值是按声明顺序执行的。

修正 3:拷贝构造中 other->_array 误用指针访问

问题:other 是 const Stack&(引用),不是指针,应该用 . 而不是 ->。

// 修改前
this->_array[i] = other->_array[i];   // ← other 是引用,不是指针

// 修改后
_array[i] = other._array[i];          // ← 用 . 访问成员

修正 4:拷贝构造循环条件用了不存在的 size() 方法

问题:other.size() 没有定义,且循环应使用 other._size。

// 修改前
for (int i = 0; i < other.size(); i++)

// 修改后
for (int i = 0; i < other._size; i++)

修正 5:拷贝赋值中 swap 使用了错误的成员名

问题:类中成员叫 _array,但 swap 写了 _a。

// 修改前
swap(_a, tmp._a);

// 修改后
std::swap(_array, tmp._array);

修正 6:拷贝赋值缺少 return *this;

问题:operator= 必须返回当前对象的引用,否则 s1 = s2 = s3 链式赋值会编译报错。

// 修改前
Stack& operator=(const Stack& other)
{
    if (this != &other)
    {
        // ... swap ...
    }
    // ← 缺少 return
}

// 修改后
Stack& operator=(const Stack& other)
{
    if (this != &other)
    {
        Stack tmp(other);
        std::swap(_array, tmp._array);
        std::swap(_size, tmp._size);
        std::swap(_capacity, tmp._capacity);
    }
    return *this;          // ← 支持链式赋值
}

修正 7:类定义末尾缺少分号

问题:类定义的 {} 后必须加分号,否则编译报错。

// 修改前
private:
    T* _array;
    int _size;
    int _capacity;
}            // ← 缺少分号

// 修改后
private:
    T* _array;
    int _size;
    int _capacity;
};           // ← 必须加分号

6. 完整修正版 Stack

#include <iostream>
#include <cassert>
#include <algorithm>
using namespace std;

template<typename T>
class Stack
{
public:
    // 构造:申请资源
    Stack(int capacity = 10)
        : _array(capacity > 0 ? new T[capacity] : nullptr)
        , _size(0)
        , _capacity(capacity)
    {
        assert(capacity > 0);
    }

    // 析构:释放资源
    ~Stack()
    {
        delete[] _array;
    }

    // 深拷贝构造
    Stack(const Stack& other)
        : _array(other._capacity > 0 ? new T[other._capacity] : nullptr)
        , _size(other._size)
        , _capacity(other._capacity)
    {
        for (int i = 0; i < other._size; i++)
        {
            _array[i] = other._array[i];
        }
    }

    // 拷贝赋值:copy-and-swap
    Stack& operator=(const Stack& other)
    {
        if (this != &other)
        {
            Stack tmp(other);
            std::swap(_array, tmp._array);
            std::swap(_size, tmp._size);
            std::swap(_capacity, tmp._capacity);
        }
        return *this;
    }

private:
    T*  _array;
    int _size;
    int _capacity;
};

7. 小结

模板初阶的主线就两条:函数模板和类模板,共享 template<typename T> 这套语法,但实例化方式不同。

  • 泛型编程是动机:函数重载解决不了"逻辑相同、类型不同"的问题;
  • 函数模板是"写一份函数,编译器根据调用时的类型生成多份"——可以隐式推演,也可以显式指定;
  • 类模板是"写一份类,编译器根据 <类型> 生成多份"——必须显式指定,Stack<int> 才是类型;
  • 匹配原则解决"非模板函数和模板函数共存时选谁"——优先选不需要额外加工的版本;
  • 工程差异:模板不能声明定义分离到 .h/.cpp,全部写头文件里是最简单的解法。

掌握模板初阶,你已经拿到了写 STL 容器的钥匙——vector、list、map 的骨架就是类模板 + 三法则。


华为OD机试2025A卷 - 分苹果(Java & Python& JS & C++ & C ) 阅读详情

相关推荐

博途 V15.1安装步骤

温馨提醒:安装此软件电脑配置至少应满足以下要求 处理器 CoreTM i5-3320M 3.3 GHz 或相当 内存 至少8G 硬盘 300 GB SSD 显示器 15.6' 宽屏显示 (1920 x 1080) 1.双击打开【Siemens v15.1】文件夹。 2.鼠标右击【STEP 7 BasicProfess...

谢谢关注“小鱼教你模数电”,各平台同号 9万+

【C++】模板初阶

本篇博客主要内容:初步接触C++模板语法。STL库的学习。不过在进入STL内容的深度学习之前,需要给大家简单补充一下C++模板部分的知识。话不多说,开始我们今天的内容。本篇博客主要介绍了模板,从C语言提供Swap功能的缺陷引入,提供了解决代码冗余和可维护性的解决方案——模板。模板分为函数模板和类模板,函数模板允许你编写一个通用的函数,该函数可以处理多种数据类型的参数。类模板允许你编写一个通用的类,该类可以处理多种数据类型的成员。同时还讲到了两种模板的定义和实例化方式,显示实例化和隐式实例化。

2303_79329831的博客 2423

【国民技术N32项目移植】基于 Windows 的 ARM GCC 开发环境

【国民技术N32项目移植】基于 Windows 的 ARM GCC 开发环境

weixin_42150032的博客 1361

【C++】模板初阶入门:什么是模板?怎么用?这篇文章帮你打通 “第一关”

C++ 模板入门专属!拆解函数 / 类模板核心用法,附直观代码示例,帮新手避开编译、类型匹配误区,快速掌握模板基础,为泛型编程打基础。

2403_86779341的博客 1605

rockchip rk3566 android11 网口log报错: DMA engine initialization failed

问题描述:上电初始化前将网口插入,然后上电初始化网口能够正常使用,且能够找到PHY,ifconfig 查看能够有eth0 产生,网口正常使用且能够热插拔,但上电初始化时,不插入网口,就会报DMA engine initialization failed 错误,DMA 初始化的时候出错了。 分析:一般产生这个问题可以认为是GMAC 的工作时钟出问题了。先测量时钟引脚是否有时钟,时钟频率以及幅度等指标是否正常,主要确认以下几个方面: 1.IOMUX 出错,检查时钟脚寄存器值是否正确。 2.时钟方向以及配置

qq_48709036的博客 2万+

解决/usr/lib64/libstdc++.so.6和/lib64/libc.so.6版本过低问题

在CentOS7.0中编译好MySQL5.7.14之后,然后在CentOS6.5中初始化MySQL,执行:/usr/local/mysql/bin/mysqld --version出现如下问题:./bin/mysqld: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./bin/mysqld)

poxiaonie的博客 2万+

模板初阶【C++】

不能简单地直接类名+::而是模板类型声明+类中的成员函数的定义[注意此时类名后面还是要加,因为这样才是一个完整的类类型],这样类实例化对象的时候就可以推导出对应的成员函数的类型由于模板类不完整,所以不能直接用它去限定作用域即在类外实现成员函数等用::区限定时,不能直接限定,要先指定类型。

2301_80058734的博客 2131

Aix升级和安装ssh配置过程

Aix升级和安装ssh配置过程Aix升级和安装ssh配置过程配置前背景配置步骤 Aix升级和安装ssh配置过程 部门IBM小型机的Aix操作系统需要进行ssh补丁修复,修复其实就是ssh版本你的升级,对升级的过程和中途遇到的问题做了记录如下。 配置前背景 操作系统: gdboss2/>uname -u //操作系统的Aix5.3 IBM,020693E5P gdboss2/&gt...

阿阿阿唱的博客 2991

C++ : 模板初阶

(这仅仅是本例的场景不太好,将a强制类型转换到double可以使编译器对a和b类型匹配都是double,这样是可以通过编译的,但是这样操作的结果是不稳定的。模板匹配的类型是double,由于全局已经有了类型是double的函数,所以编译器不会再通过函数模板再生成一份swap函数。本例中,由于a,b两个变量的类型不同,所以编译器会优先选择两个模板参数的swap函数,而不是一个模板参数的swap。模板参数的类型,如上例,a,b的类型都是整形,所以编译器会根据根据函数的模板生成一份函数,这份函数仅仅将。

石墨博客 1307

秋招突击——笔试整理——8/25——拼多多笔试整理

继续加油! 转变思维,有简单到复杂,由直观到抽象,这种贪心模拟的题目一直是这样的!

Blackoutdragon的博客 2400

2025年11月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(二)

本文整理了2025年11月架构设计师考试的16-30题真题及解析,涵盖多个计算机系统知识领域。主要内容包括:Linux绝对路径以根目录开始(A);模块调用下层数量用扇出表示(B);游乐园问题通过集合运算得出10人未游玩(B);磁盘位示图计算为200字节(C);测试用例核心是输入与预期输出(D)。其他题目涉及软件可靠性测试概念(B错)、评价程序准确度排序(真实程序最高,A)、测试阶段顺序(单元→组装→系统,A)、Web服务器压力测试前置步骤(C)、分时系统响应时间影响因素(用户数量多,A)、逆向工程方法(基于

一几文的编程学习笔记。 225

C++模板初阶

泛型编程与函数模板 泛型编程通过模板实现代码复用,避免为不同类型重复编写相同逻辑的函数。函数模板允许定义与类型无关的通用函数,编译器根据实参类型自动生成特定版本的代码。例如,Swap模板可处理int、double等类型。模板实例化分为隐式(自动类型推导)和显式(手动指定类型)。调用时优先匹配非模板函数,若模板能生成更匹配的版本则选择模板。类模板同理,需显式实例化为具体类型(如Stack<int>)。模板不支持隐式类型转换,但能显著提升代码复用性和可维护性。

jingfeng514的博客 1392

【AIZ智能体】Codex 全能AI助手从部署到使用实战操作详解

Codex 全能AI助手从部署到使用实战操作详解

congge 2983

【C++】初阶模板

有关C++初阶模板,内容包括:泛型编程简介,函数模板的定义,实现原理,多个模板参数,实例以及类模板相关内容.

C/C++领域,数据结构,linux,MySQl学习者 1389

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【C++】模板初阶 —— 泛型编程

真的不点进来看看吗?【C++】模板初阶新鲜出炉,文章不长,简单介绍模板来过渡进入STL的学习

qq_42996461的博客 1049

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【C++】模板(初阶)

模板与泛型编程是C++中实现代码复用的重要手段。通过模板,可以编写与类型无关的通用代码,避免为每种类型重复编写逻辑相同的函数。模板分为函数模板和类模板。函数模板允许定义一个通用的函数,编译器根据传入的参数类型自动生成对应的函数实例。例如,通过模板可以实现一个通用的交换函数,避免了为每种类型编写重载函数。类模板则允许定义通用的类,如栈模板,可以根据不同的类型生成对应的类实例。模板的使用提高了代码的复用性和可维护性,减少了代码冗余。

2401_87820834的博客 1820

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【c++】模板初阶

一、泛型编程 二、函数模板 1. 函数模板的概念 2. 函数模板格式 3. 函数模板的原理 4. 函数模板的实例化 1. 隐式实例化 4. 显示实例化 5. 模板函数匹配原则 三、类模板 1. 类模板的定义格式 2. 类模板的实例化

重铸自身根基的博客 1186

C++ 模板初阶

本文系统介绍了C++中的泛型编程与模板技术。主要内容包括:1)泛型编程概念,通过参数化类型实现代码复用和类型安全;2)函数模板的语法、原理(编译时类型推导生成具体函数)及实例化方式(隐式/显式);3)类模板的定义格式和实例化特点(需显式指定类型参数)。重点对比了模板与函数重载的优劣,分析了模板参数匹配优先级(优先选择普通函数),并指出模板不支持自动类型转换的特性。文章通过交换函数和栈类示例,展示了模板在通用数据结构和算法中的典型应用。

naobeng的博客 1732

【C++初阶】想要编译器为你干活吗?来试试模板吧(模板初阶)

【C++初阶】想要编译器为你干活吗?来试试模板吧(模板初阶)

旋律 | 话语 | 色彩 | 情景 604

C++ 模板初阶 —— 编写“万能代码”的魔法模具

📝 C++模板初阶摘要 模板是C++泛型编程的核心工具,通过类型参数化实现代码复用。 🔹 函数模板 使用template<typename T>定义通用函数 编译器根据调用类型自动实例化(隐式/显式) 与普通函数共存时优先匹配普通函数 🔹 类模板 整个类可参数化(如Stack<T>) 需将声明和定义都放在头文件中 💡 优势 消除重复代码 提高可维护性 支持任意兼容类型 ⚠️ 注意 类模板不能分离编译 类型推导需一致 模板为STL等高级特性奠定基础,是掌握现代C++的关键。

potato_may的博客 1602

【C++同步练习】模板初阶

摘要:本文系统回顾了C++模板的核心概念与应用。函数模板通过类型参数实现代码复用,强调类型推导规则和语法要点(如typename/class声明)。类模板作为参数化类蓝图,支持多参数和特化,需注意实例化规则。通过典型例题解析,指出常见误区:函数模板不支持隐式类型转换(B选项错误),类模板在运行时不会检查类型(C选项错误),模板类与普通类的编译处理不同(D选项错误)。正确模板声明需每个参数前加typename/class(4/6/7选项正确)。模板机制本质是编译时实例化,兼具类型安全与高效性。

小年糕的博客 2002

c++模板初阶

c++模板期待大家观看

includeevey的博客 1308

【C++】深入 C++ 模板特化与非类型参数:提高代码效率的进阶指南

C++ 的模板功能极为强大,除了实现常见的泛型编程,模板还允许我们定义更加灵活和高效的代码。本文将深入讲解 C++ 中的非类型模板参数和模板特化,并结合代码示例展示这些高级模板特性的用法和适用场景。public:// 模板函数实现这样,任何包含的文件都可以直接实例化模板,从而避免链接错误。本文介绍了 C++ 模板的进阶用法,包含非类型模板参数、模板特化、全特化与偏特化的概念。通过这些模板特性,我们可以编写出更加灵活和高效的泛型代码,满足复杂的编程需求。

weixin_74837455的博客 2229
上一篇: C++ 内存管理 —— new/delete 到底比 malloc/free 多做了什么?
DDXYcoder
博客等级 码龄1年 140粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值