当前位置: 首页 > news >正文

C++模板及实战,以及重载运算符

C++ 模板函数、lambda 与运算符重载:从一个 vector 过滤器开始

今天学的是 C++ 里比较“工程化”的几个语法点:

  1. 模板函数 template<typename T>
  2. 函数参数的自动推导
  3. const T & 引用传参
  4. lambda 表达式
  5. 运算符重载 operator|
  6. | 写出类似管道的过滤操作
  7. 三目运算符与 std::common_type_t

核心代码如下:

#include<bits/stdc++.h>
template<typename T> void print(const T &vec)
{for(const auto &item:vec){std::cout<<item<<'\n';}
}
template<typename Tu,typename Func> std::vector<Tu> operator| (const std::vector<Tu> &a,const Func &func)
{std::vector<Tu> res;for(const auto &b:a){if(func(b)) res.emplace_back(b);}return res;
}
int main()
{std::vector<int> init;for(int i=1;i<=100;++i) init.emplace_back(i);auto is_even=[](int i){return (i%2==0);};print(init|is_even);
}

这段代码的作用是:

1~100 里筛出所有偶数,然后打印。

输出大概是:

2
4
6
...
100

1. 模板函数:让函数适配多种类型

首先是这个打印函数:

template<typename T> void print(const T &vec)
{for(const auto &item:vec){std::cout<<item<<'\n';}
}

这里:

template<typename T>

表示 T 是一个模板类型。

调用时,编译器会自动根据实参推导 T 是什么。

比如:

std::vector<int> a;
print(a);

那么编译器推导:

T = std::vector<int>

所以这个函数等价于被实例化成:

void print(const std::vector<int> &vec)
{for(const auto &item:vec){std::cout<<item<<'\n';}
}

模板函数的本质不是运行时判断类型,而是编译期生成对应版本的函数


2. 为什么参数写成 const T &

void print(const T &vec)

这里有两个重点。

第一,& 表示引用传参。

如果不写引用:

void print(T vec)

那么传入一个 vector 时,会复制整个 vector

如果 vector 很大,这个复制成本就很高。

写成:

const T &vec

表示:

不复制,只引用原对象

第二,const 表示函数内部不能修改 vec

所以:

const T &vec

就是:

不拷贝,也不修改

这是读数据时很常见的写法。


3. range-based forconst auto &

代码里还有:

for(const auto &item:vec)

它的意思是:

遍历 vec 里面的每个元素。

其中:

auto

让编译器自动推导元素类型。

如果 vec 是:

std::vector<int>

那么:

item

就是 int 类型。

写成:

const auto &item

表示:

不复制元素,也不修改元素

虽然 int 复制成本很低,但如果元素是 string、结构体、类对象,用引用就更重要。


4. lambda 表达式:临时写一个函数对象

这里:

auto is_even=[](int i)
{return (i%2==0);
};

定义了一个 lambda。

可以理解成一个临时的小函数:

bool is_even(int i)
{return i%2==0;
}

但 lambda 本质上不是普通函数,而是一个编译器生成的匿名类对象。

大概可以理解成:

struct Lambda
{bool operator()(int i) const{return i%2==0;}
};

然后:

auto is_even=[](int i)
{return (i%2==0);
};

大概相当于:

Lambda is_even;

所以后面可以写:

is_even(2)

本质类似于:

is_even.operator()(2)

5. 重载 operator|

最关键的是这个函数:

template<typename Tu,typename Func> std::vector<Tu> operator| (const std::vector<Tu> &a,const Func &func)
{std::vector<Tu> res;for(const auto &b:a){if(func(b)) res.emplace_back(b);}return res;
}

它重载了 | 运算符。

原本 | 是按位或,比如:

int x=3|5;

但现在我们给 std::vector<Tu> 和某个函数对象之间定义了新的含义。

当代码写:

init|is_even

编译器会把它理解成:

operator|(init,is_even)

也就是说:

init

传给第一个参数:

const std::vector<Tu> &a

而:

is_even

传给第二个参数:

const Func &func

6. 模板参数怎么推导

调用:

init|is_even

其中:

init

类型是:

std::vector<int>

所以:

const std::vector<Tu> &a

会推导出:

Tu = int

is_even 是一个 lambda 对象,所以:

Func = lambda 的匿名类型

于是这个模板函数大概实例化成:

std::vector<int> operator|(const std::vector<int> &a,const Lambda &func)
{std::vector<int> res;for(const auto &b:a){if(func(b)) res.emplace_back(b);}return res;
}

7. func(b) 是什么

循环里:

if(func(b)) res.emplace_back(b);

其中:

func

其实就是 is_even 的引用。

所以:

func(b)

等价于:

is_even(b)

也就是判断:

b%2==0

如果是偶数,就加入结果数组:

res.emplace_back(b);

最后:

return res;

返回筛选后的新 vector


8. init|is_even 的整体过程

这句:

print(init|is_even);

可以拆成:

auto tmp=operator|(init,is_even);
print(tmp);

也就是:

std::vector<int> tmp;
for(const auto &b:init)
{if(is_even(b)) tmp.emplace_back(b);
}
print(tmp);

所以本质是一个 filter 操作。

如果不用运算符重载,可以写成普通函数:

template<typename Tu,typename Func> std::vector<Tu> filter(const std::vector<Tu> &a,const Func &func)
{std::vector<Tu> res;for(const auto &b:a){if(func(b)) res.emplace_back(b);}return res;
}

调用:

print(filter(init,is_even));

而现在用 operator| 后,可以写成:

print(init|is_even);

更像“管道”。


9. emplace_backpush_back

代码里用了:

res.emplace_back(b);

对于 int 来说,emplace_back(b)push_back(b) 差别不大。

但如果元素是结构体或类对象:

struct node
{int x,y;node(int x,int y):x(x),y(y){}
};

那么:

vec.emplace_back(1,2);

可以直接在 vector 内部构造对象。

而:

vec.push_back(node(1,2));

一般是先构造临时对象,再放进 vector

所以 emplace_back 更偏工程习惯。


10. 今天还学了三目运算符和 common_type_t

三目运算符:

条件 ? 表达式1 : 表达式2

比如:

return a>b?a:b;

等价于:

if(a>b) return a;
else return b;

但三目运算符有一个重要特点:

它自己也有类型。

比如:

auto x=true?1:2.5;

虽然条件是 true,但是编译器仍然会同时看 12.5 的类型。

1int2.5double

最终共同类型是:

double

所以:

x

double


11. true?Tp{}:Up{} 是什么

在模板里有时会看到:

decltype(true?Tp{}:Up{})

它的意思是:

构造一个 Tp 类型的临时对象和一个 Up 类型的临时对象,然后放进三目运算符里,让编译器判断这两个类型混在一起后会变成什么类型。

比如:

decltype(true?int{}:double{})

结果是:

double

因为 intdouble 的共同类型是 double

这里的 true 不是重点。

重点是三目运算符会根据第二、第三个表达式推出一个统一类型。


12. std::common_type_t<Tp,Up>

更正规的方法是:

std::common_type_t<Tp,Up>

它表示:

TpUp 的共同类型。

比如:

std::common_type_t<int,double>

结果是:

double
std::common_type_t<int,long long>

结果一般是:

long long

所以可以写一个支持不同类型参数的 Max

#include<bits/stdc++.h>
template<typename Tp,typename Up>
std::common_type_t<Tp,Up> Max(const Tp &a,const Up &b)
{return a>b?a:b;
}
int main()
{auto x=Max(1,2.5);std::cout<<x<<'\n';
}

这里:

Max(1,2.5)

推导出:

Tp = int
Up = double

返回类型是:

std::common_type_t<int,double>

也就是:

double

13. 今天的核心收获

今天的代码可以总结成一句话:

用模板函数让代码适配多种类型,用 lambda 传入筛选规则,用运算符重载把 filter(vec,func) 写成了 vec|func

具体学到的点:

template<typename T>

表示定义模板类型。

const T &

表示引用传参,不拷贝,也不修改。

auto is_even=[](int i)
{return i%2==0;
};

表示定义一个 lambda 函数对象。

operator|(init,is_even)

等价于:

init|is_even

而:

func(b)

就是调用传进来的 lambda。

最后:

print(init|is_even);

看起来像一条管道:

原数组 | 筛选条件 | 输出

虽然底层仍然是普通函数调用,但写法更接近现代 C++ 的函数式风格。

http://www.zskr.cn/news/1535292.html

相关文章:

  • 临汾市_临汾市奢侈品回收门店红黑榜:综合实力最强的五家店铺推荐 - 奢金汇
  • Telegraph Webhook 完全指南:实现实时消息处理与事件响应
  • 申论笔记pdf百度云|网盘|电子版
  • 临沂市_临沂市奢侈品手表包包回收价格差距高达15%:实测对比告诉你哪家店报价最实在 - 奢金汇
  • 一个被忽略的行草范本:傅山这轴六言诗,藏着“行气不断”的密码,新手也能用
  • 小白程序员必看:收藏这份交通大模型(8大领域+116案例)学习指南
  • 收藏!小白程序员必备:大模型时代黄金三年,5大AI岗位助你逆势起飞!
  • 七台河市_2026年七台河市奢侈品手表包包回收门店权威测评:这五家店铺回收价格最高 - 奢金汇
  • 大模型MoE稀疏激活原理与工程实践全解析
  • 3分钟快速安装:Figma中文汉化插件完整指南
  • 资管档案数智化实战:如何利用AI Agent解决RAG知识库与行业制度的同步难题?
  • 倾转旋翼VTOL无人机的高保真6自由度纵向飞行动力学模拟器和闭环GNC堆栈,稳定悬停保持LQR、动态控制混合和固定翼巡航MATLAB 和 Simulink
  • GR3六轴工业协作机械臂GR3六轴工业协作机械臂技术档案摘要(601-616) 该文档详细介绍了GR3机械臂的核心控制算法和功能模块实现,主要包括: 运动控制:采用自适应终端滑模控制实现高精度轨迹
  • 制造业汽车零配件EDI软件场景方案
  • 人工智能与数据科学:关系、差异与未来展望
  • 三步掌握Python通达信数据接口:MOOTDX让量化分析变简单
  • 2026企业级AI Agent选型实战:深度拆解安全审计与信创适配核心指标
  • C#调用本地大模型实战:Ollama+Qwen零成本集成指南
  • 学习率可视化分析:梯度下降中的油门与刹车
  • 大模型面试必备11-InfoNCE loss 和 Cross Entropy Loss
  • 2026苏州市家用空调-中央空调等维修安装移机加氟-本地精选指南 -欧米到家 - 欧米到家
  • 宜宾卖黄金别踩亏2026年6月回收价格与正规门店测评 - 余生黄金回收
  • Gemma 4外贸实战指南:本地部署、选型与7个即用Prompt模板
  • D3KeyHelper:暗黑3玩家的智能战斗伙伴,告别手酸专注策略
  • # 智融PD芯片I2C通信实战:从零实现高效可靠的模拟I2C驱动
  • 终极指南:5分钟用Qt Material打造现代化桌面应用界面
  • 2026年6月昭通黄金回收靠谱商家筛选与变现避坑干货 - 余生黄金回收
  • 网盘直链下载助手终极指南:一键获取九大网盘真实下载地址的技术解决方案
  • AI Agent如何处理无预设流程业务?深度解析大模型自主规划的底层推理能力与架构落地实践
  • [特殊字符]️【万字深度干货】数字政府数字化转型规划信息化解决方案全解析——从国土空间规划重构到智能审批的完整技术路线(PPT)