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

C++11 -- emplace、包装器

目录

一、可变模板参数

1、基本认识

2、C++11 -- emplace系列

二、包装器

2.1、function

2.2、bind


一、可变模板参数

1、基本认识

想想我们平常用的库里面有哪些用到了可变参数。printf、scanf都是可变参数

        语法上,三个点表示我们可以写很多参数,如 printf("%d%d%d", x, y, z); 不管调用时实际传递了多少参数,函数都能接收过来,并再函数内部根据第一个参数(通常是格式字符串)的指示,对这些传入的参数进行解析和访问。

        底层其实是,这些可变参数通常是连续存储再栈内存中的。函数内部通过特定的宏(如va_start, va_arg, va_end)配合一个指向栈上参数起始地址位置的指针(va_list)来逐个访问这些参数,并根据需要转换它们的数据类型。

而C++需要模板参数,模板参数传递的是类型。让我们想传几个就传几个

看看C++11的模板的可变参数:

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args... args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

如上,如果参数包里有一个类型,函数的参数列表里就定义了一个形参,参数包两个类型,函数两个形参,以此类推。可以通过 sizeof...(args) 查看参数包中参数的个数

// 模板可变参数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << sizeof...(args) << endl; // 查看参数包中参数的个数// 但是不支持这样访问参数包中的参数,参数包不是数组或容器/*for (size_t i = 0;i < sizeof...(args);i++){cout << args[i] << endl;}*/
}int main()
{ShowList(1);ShowList(1, 2.2);ShowList(1, 3.3, "xxxxx");return 0;
}

那么,如何获取参数包中的参数呢,如下:

template<class T>
void ShowList(T val)
{cout << val << " ";cout << endl;
}// 模板可变参数
template <class T, class ...Args>
void ShowList(T val, Args... args)
{cout << val << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 2.2);ShowList(1, 3.3, "xxxxx");return 0;
}

上面实际中不常用,了解一下就够了。

主要看下面这种场景:

可以看出,可以用参数包来构造对象


2、C++11 -- emplace系列

在很多STL容器中,C++11还提供了emplace、emplace_back接口,如下:

emplace_back:

  • 在序列容器(如 vector、deque、list)的尾部直接构造元素,无需创建临时对象。

emplace:

  • 在任意位置(如 vector、list)或关联容器(如 map、set)中直接构造元素。对于 map,它构造的是 std::pair<const Key, Value>
int main()
{std::list<pair<int, char>> mylist;// 平常我们往容器插入数据,都是push_back,push_front之类的...mylist.push_back(make_pair(40, 'a')); // 拷贝构造pairmylist.push_back({50, 'b'}); // 拷贝构造pair// C++11mylist.emplace_back(10, 'a'); // 直接调pair的构造,相当于直接在节点构造pairmylist.emplace_back(make_pair(20, 'b')); // 调pair的拷贝构造return 0;
}

emplace_back利用参数包往下传,传给了类似上面的 Date* ret = new Date(args...),args再往后传调构造。看起来跟push_back没啥区别,看下面的场景:

#include <iostream>
#include <list>using namespace std;class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){std::cout << "构造" << std::endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){std::cout << "拷贝构造" << std::endl;}private:int _year;int _month;int _day;
};int main()
{std::list<Date> mylist;mylist.emplace_back(2025, 7, 26);mylist.push_back(Date(2025, 7, 26));return 0;
}

显而易见,emplace利用模板的可变参数,讲参数直接往下传,直接构造Date;而push_back先构造一个临时对象,在调用拷贝构造。从上看出,emplace_back是个更强大的push_back,在深拷贝上,效率高于push_back。


二、包装器

  • C++中的可调用对象包括,函数指针,仿函数,lambda表达式

包装器是什么,为什么需要包装器?

        包装器是 C++11 引入的一种特殊工具类,用于将不同类型的可调用实体封装成统一接口的对象。

        需要包装器的目的是为了统一可调用对象的类型。比如我们想把可调用对象存到容器中去,如果没有包装器,你写函数指针就只能调用函数指针指向的函数,你写仿函数只能调用仿函数提供的函数。

2.1、function

使用function要包含头文件 <functional>

头文件<functional>class function<Ret(Args...)>;模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
double f(double i)
{return i / 4;
}
struct Func
{double operator()(double d){return d / 5;}
};int main()
{// 包装器 -- 解决可调用对象的类型问题function<double(double)> f1 = f;function<double(double)> f2 = [](double a)->double {return a / 4;};function<double(double)> f3 = Func();// vector类型是包装器,可接收函数指针,仿函数,lambdastd::vector<function<double(double)>> v = {f1, f2, f3};double n = 2.5;for (auto fun : v){std::cout << fun(n++) << std::endl; // 遍历调用函数指针,lambda,仿函数}return 0;
}

2.2、bind

        std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接收一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

        一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M 可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

一般形式:
auto newCallable = bind(callable,arg_list);

newCallable表示一个新的可调用对象,bind绑定callable作为可调用对象,参数为arg_list。返回一个新的可调用对象。

具体用法:

placeholders是一个命名空间,里面的_1,_2是用来绑定参数的

可以发现bind绑定的参数不同,形成的可调用对象不同,执行的结果不同。

http://www.dtcms.com/a/300232.html

相关文章:

  • 标准库开发和寄存器开发的区别
  • nfls dp 刷题 题解
  • AutoCAD_2025下载与保姆级安装教程
  • 【安全漏洞】防范未然:如何有效关闭不必要的HTTP请求方法,保护你的Web应用
  • cursor使用方法
  • MGER作业
  • Python 面向对象基础
  • 26考研英语词汇的逻辑笔记
  • 【PCIe 总线及设备入门学习专栏 5.1.3 -- PCIe PERST# 时序要求】
  • 从治理到共情——平台伦理的乡村共建之路
  • DeepSeek-LLM模块解析
  • 多项目终端环境初始化开发环境方案
  • 【RHCSA 问答题】第 10 章 配置和保护 SSH
  • cacti漏洞CVE-2022-46169的复现
  • 界面规范2-列表上
  • NTLite Ent Version
  • 文件IO——bmp图像处理
  • GRE、MGRE实验
  • 机器学习(重学版)基础篇(概念与评估)
  • 算子推理是什么
  • GStreamer中Pipeline(管道)
  • 速通python加密之base64
  • 《C++》模板详解
  • odoo代码分析(二)
  • 一键实现推送服务:Docker部署ntfy开源通知系统指南
  • STM32-定时器的基本定时/计数功能实现配置教程(寄存器版)
  • 数据结构习题--删除排序数组中的重复项
  • Linux内核设计与实现 - 第13章 虚拟文件系统(VFS)
  • TCP如何解决网络切换问题
  • Flutter开发实战之原生平台集成