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

【C++11】可变模板参数

文章目录

  • 可变模板参数的概念
    • 递归函数方式展开参数包
  • STL容器中的empalce相关的接口函数
  • emplace 与 insert / push_back 的区别




可变模板参数的概念


可变参数模板是 C++11 引入的一种模板特性,允许定义可以接收任意数量参数的模板,广泛应用于函数和类的设计中,以实现灵活和通用的代码结构。

可变参数模板可以处理不定数量的模板参数。它的语法使用了三个点(...)来表示模板参数包。这使得你可以创建一个模板,它可以适应任意数量的类型或值。

语法形式

template<typename... Args>
void func(Args... args) {// 函数体
}
  • Args... 表示一个模板参数包,它可以是任意数量的类型参数。
  • args... 表示一个函数参数包,可以对应多个具体的实参。

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N >= 0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

递归函数方式展开参数包

// 递归终止条件:没有参数的版本
void print() {std::cout << "End of recursion" << std::endl;
}// 可变参数模板版本:处理多个参数
template<typename T, typename... Args>
void print(T first, Args... rest) {std::cout << first << " ";print(rest...); // 递归调用,逐个处理参数
}int main() {print(1, 2, 3, "Hello", 4.5);return 0;
}

在这里插入图片描述

  • print() 是递归的终止条件,用于处理没有参数的情况。
  • 模板函数 print(T first, Args... rest) 通过递归调用自身来依次处理每个参数,直到只剩下一个参数。


STL容器中的empalce相关的接口函数


在 C++ 的标准模板库(STL)中,emplace 系列接口提供了一种更高效的方式将元素添加到容器中。emplace 的主要优势是,它可以直接在容器内部构造元素,避免了额外的拷贝或移动操作。因此,相比于使用 insertpush_back 这样的函数,emplace 系列接口在某些情况下更高效。(但也高效不了多少,因为移动操作的代价足够小)

https://cplusplus.com/reference/vector/vector/emplace_back/

template <class... Args>
void emplace_back (Args&&... args)

emplace 系列函数的主要作用是原地构造元素,而不是首先构造一个临时对象再复制或移动到容器中

原地构造:使用 emplace,你可以提供构造元素所需的参数,容器会直接在内部空间中构造该对象,避免了先创建对象再将其复制到容器的过程。
避免不必要的拷贝/移动:与 push_back() 等函数相比,emplace 可以减少临时对象的构造以及拷贝/移动操作,这在性能敏感的场景中很有帮助。

在这里插入图片描述
hyt::string是我自己模拟实现的一个string类,可以看到,我们使用emplace系列的接口,确实可以将移动拷贝省略掉。能提高一点效率,但其实移动构造的效率并不大,下面我分享一下我自己模拟写的emplace系列接口:

// 模拟实现list在之前的章节有提过,这里只是将原来的代码多增加一些接口的片段代码// 这是list需要用到的节点类
template<class T>
struct __list_node
{__list_node(const T& val = T()):_data(val), _prev(nullptr), _next(nullptr){}// 这里需要在原来的基础上需要增加一个可变模板参数模板的构造函数,方便下面使用newtemplate<class ...Args>__list_node(Args&& ...args): _data(std::forward<Args>(args)...), _prev(nullptr), _next(nullptr){}T _data;__list_node* _prev;__list_node* _next;
};template<class T>
struct list
{template<class ...Args>iterator emplace(iterator position, Args&&... args){node* cur = position._node;node* prev = cur->_prev;// 函数参数包的完美转发node* newnode = new node(std::forward<Args>(args)...);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(cur);}template<class ...Args>void emplace_back(Args&&... args){// 函数参数包的完美转发emplace(end(), std::forward<Args>(args)...);}private:__list_node<T>* _head; // 指向节点类的指针// 获取节点函数,这里更新成了万能引用版的template<class T>node* get_node(T&& val = T()){node* new_node = new node(std::forward<T>(val)); // 完美转发new_node->_prev = new_node;new_node->_next = new_node;return new_node;}
}


emplace 与 insert / push_back 的区别

最后再来总结一下:何时使用 emplace
效率优先:如果添加对象时想避免额外的构造和拷贝,emplace 通常是更优的选择。
构造复杂对象:当元素的构造比较复杂时,emplace 可以让代码更简洁,直接传入构造参数即
在这里插入图片描述


http://www.mrgr.cn/news/45009.html

相关文章:

  • redis过期策略和内存淘汰机制
  • 基于SpringBoot“花开富贵”花园管理系统【附源码】
  • 基于SpringBoot博物馆游客预约系统【附源码】
  • vue2和vue3中的组件间通信知识点总结
  • 深入理解 JavaScript 中的表达式、运算符、语句和声明概念
  • 二进制求和
  • Bianchi模型、python计算及ns3验证_关于E[P*]的补充
  • Kubernetes 洞察:DaemonSet 全解析
  • Facebook 正式推出了一项专为 Z 世代设计的全新改版
  • 【时间盒子】-【9.任务设置项】自定义任务名称、任务时长等设置项组件
  • 软件测试比赛-学习
  • github项目学习——ruoyi-vue-pro
  • 音视频入门基础:FLV专题(14)——FFmpeg源码中,解码Script Tag的实现
  • 基于Python的美术馆预约系统【附源码】
  • [Algorithm][贪心][合并区间][无重叠区间][用最少数量的箭引爆气球]详细讲解
  • 数据结构 ——— 相交链表(链表的共节点)
  • CART回归树中的 方差减少量 计算步骤和示例
  • Ancient City Ruins 古代城市遗址废墟建筑游戏场景
  • 在数据中,如何删除表中的记录?
  • Cesium的一些神奇概念及技术流程(1)