C++ 23 实用工具(一)

news2024/12/25 1:45:32

C++ 23 实用工具(一)

工具函数是非常有价值的工具。它们不仅可以用于特定的领域,还可以应用于任意值和函数,甚至可以创建新的函数并将它们绑定到变量上。

常用函数

你可以使用各种变体的 minmaxminmax 函数来对值和初始化列表进行操作。这些函数需要头文件 <algorithm>。相反,std::movestd::forwardstd::to_underlyingstd::swap 函数则定义在头文件 <utility> 中,你可以将它们应用于任意值中。

以上是本章节中提供的一些实用工具函数和库,可以在各个领域和场景中灵活使用。希望这些函数和库能够帮助你更加高效地开发和管理项目。

std::min、std::max和std::minmax

在 C++ 的 <algorithm> 头文件中,有三个非常有用的函数:std::minstd::maxstd::minmax。它们可以作用于值和初始化列表,并将所请求的值作为结果返回。对于 std::minmax 函数,你会得到一个 std::pair,其中第一个元素是最小值,第二个元素是最大值。默认情况下使用小于运算符(<),但你可以应用自己的比较运算符。这个函数需要两个参数并返回一个布尔值。这种返回 truefalse 的函数称为谓词。

int main()
{
	std::cout << std::min(2011, 2022) << std::endl; // 2011
	std::cout << std::min({3, 1, 2011, 2022, -5}) << std::endl; // -5
	std::cout << std::min(-10, -5, [](int a, int b) { return std::abs(a) < std::abs(b); }) << std::endl; // -5

	auto pairInt = std::minmax(2011, 2022);
	auto pairSeq = std::minmax({3, 1, 2011, 2022, -5});
	auto pairAbs = std::minmax({3, 1, 2011, 2022, -5}, [](int a, int b) { return std::abs(a) < std::abs(b); });

	std::cout << pairInt.first << "," << pairInt.second << std::endl; // 2011,2022
	std::cout << pairSeq.first << "," << pairSeq.second << std::endl; // -5,2022
	std::cout << pairAbs.first << "," << pairAbs.second << std::endl; // 1,2022

	return 0;
}

image-20230411153725929

函数描述
min(a, b)返回 ab 中的较小值。
min(a, b, comp)根据谓词 comp 返回 ab 中的较小值。
min(initializer list)返回初始化列表中的最小值。
min(initializer list, comp)根据谓词 comp 返回初始化列表中的最小值。
max(a, b)返回 ab 中的较大值。
max(a, b, comp)根据谓词 comp 返回 ab 中的较大值。
max(initializer list)返回初始化列表中的最大值。
max(initializer list, comp)根据谓词 comp 返回初始化列表中的最大值。
minmax(a, b)返回 ab 中的较小值和较大值。
minmax(a, b, comp)根据谓词 comp 返回 ab 中的较小值和较大值。
minmax(initializer list)返回初始化列表中的最小值和最大值。
minmax(initializer list, comp)根据谓词 comp 返回初始化列表中的最小值和最大值。

std::midpoint 和 std::lerp

std::midpoint(a, b) 函数计算 ab 的中点。ab 可以是整数、浮点数或指针。如果 ab 是指针,则必须指向同一数组对象。std::midpoint 函数需要头文件 <numeric>

std::lerp(a, b, t) 函数计算两个数的线性插值。它需要头文件 <cmath>。返回值为 a + t(b - a)

线性插值是一种常见的数值分析技术,用于在两个已知数据点之间估算未知点的值。它可以用于许多应用程序,例如图像处理、计算机图形学和动画。其中一个常见的用途是在两个颜色之间进行插值,以创建渐变效果。

#include <iostream>
#include <numeric>
#include <cmath>

int main()
{
	// midpoint
	std::cout << std::midpoint(10, 20) << std::endl; // 15
	std::cout << std::midpoint(10.5, 20.5) << std::endl; // 15.5

	int arr[] = {1, 2, 3, 4, 5};
	int* p1 = &arr[0];
	int* p2 = &arr[4];
	std::cout << *std::midpoint(p1, p2) << std::endl; // 3

	// lerp
	std::cout << std::lerp(10, 20, 0.5) << std::endl; // 15
	std::cout << std::lerp(10.5, 20.5, 0.5) << std::endl; // 15.5

	return 0;
}

image-20230411154217108

std::cmp_equal, std::cmp_not_equal, std::cmp_less, std::cmp_greater, std::cmp_less_equal 和 std::cmp_greater_equal

头文件 <utility> 中定义的函数 std::cmp_equalstd::cmp_not_equalstd::cmp_lessstd::cmp_greaterstd::cmp_less_equalstd::cmp_greater_equal 提供整数的安全比较。安全比较意味着负有符号整数与无符号整数进行比较时,负有符号整数的比较小于无符号整数,并且除有符号或无符号整数之外的其他值的比较会在编译时出错。

以下是一个代码片段,演示了有符号/无符号比较的问题。

-1 < 0u; // true
std::cmp_greater(-1, 0u); // false

将 -1 视为有符号整数时,它被提升为无符号类型,这导致了一个令人惊讶的结果。

std::move

头文件 <utility> 中定义的 std::move 函数授权编译器移动资源。在所谓的移动语义中,源对象的值被移动到新对象中。之后,源对象处于一个定义良好但不确定的状态。大多数情况下,这是源对象的默认状态。使用 std::move,编译器将源参数转换为右值引用:static_cast<std::remove_reference<decltype(arg)>::type&&>(arg)。如果编译器无法应用移动语义,则回退到复制语义:

#include <utility>
// ...
std::vector<int> myBigVec(10000000, 2011);
std::vector<int> myVec;
myVec = myBigVec; // 复制语义
myVec = std::move(myBigVec); // 移动语义

移动语义可以提高程序的性能,因为它避免了不必要的内存分配和复制操作。但是请注意,移动语义只适用于具有可移动的资源的对象,例如指针、文件句柄和大型数组。对于简单类型,移动语义通常不会提供任何性能优势。

std::forward

std::forward是定义在头文件中的一个函数,它可以让你编写函数模板,以完全相同的方式转发它们的参数。使用std::forward的典型用例是工厂函数或构造函数。工厂函数创建一个对象,因此应该传递它们的参数而不作任何修改。构造函数通常使用它们的参数来使用相同的参数初始化它们的基类。因此,std::forward是通用库作者的完美工具。

createT是一个函数模板,它必须将它们的参数作为通用引用(Args&&… args)接收。通用引用或转发引用是一个类型推断上下文中的右值引用。std::forward与可变模板一起使用,可以定义完全通用的函数模板。您的函数模板可以接受任意数量的参数并将它们不变地转发。以下是createT的示例用法:

#include <utility>
using std::initialiser_list;

struct MyData{
    MyData(int, double, char){};
};

template <typename T, typename... Args>
T createT(Args&&... args){
    return T(std::forward<Args>(args)... );
}

int a = createT<int>();
int b = createT<int>(1);
std::string s = createT<std::string>("Only for testing.");
MyData myData2 = createT<MyData>(1, 3.19, 'a');
typedef std::vector<int> IntVec;
IntVec intVec = createT<IntVec>(initialiser_list<int>({1, 2, 3}));

以上是使用std::forward和createT示例的代码。这段代码定义了一个MyData结构体以及一个createT函数模板,用于创建各种类型的对象。createT函数模板使用std::forward将其参数转发到T类型的构造函数中,以创建T类型的对象。这使得createT函数模板可以用于创建任何类型的对象,从基本类型到自定义类型。

#include <utility>
#include <vector>
#include <string>
#include <initializer_list>

using std::initializer_list;

struct MyData
{
	MyData(int, double, char)
	{
	};
};

template <typename T, typename... Args>
T createT(Args&&... args)
{
	return T(std::forward<Args>(args)...);
}

int main()
{
	int a = createT<int>();
	int b = createT<int>(1);
	std::string s = createT<std::string>("Only for testing.");
	MyData myData2 = createT<MyData>(1, 3.19, 'a');
	typedef std::vector<int> IntVec;
	IntVec intVec = createT<IntVec>(initializer_list<int>({1, 2, 3}));

	return 0;
}

使用std::forward实现完全通用的函数模板

在 C++ 中,使用模板可以实现通用的函数,但是当函数需要接受任意类型的参数时,需要使用可变模板参数(variadic templates)和通用引用(universal reference)。

通用引用是指在类型推导上下文中的右值引用,其语法为 Args&&… args。它可以接受任意类型(左值或右值)的参数,并保持完整性。在函数模板中使用通用引用时,需要使用 std::forward 函数来实现参数的完美转发。

std::forward 是一个 C++11 的模板函数,它可以将一个参数以右值或左值的形式进行转发。通常用于在一个函数中将参数转发到另一个函数,以实现参数的完美转发。

使用 std::forward 和可变模板参数,我们可以定义完全通用的函数模板。函数模板可以接受任意数量和类型的参数,并将它们完美转发到另一个函数中。这样可以大大提高代码的复用性和灵活性。

std::to_underlying

在 C++23 中,函数 std::to_underlying 将枚举类型 enum 转换为其基础类型。这个函数是一个便利函数,其表达式为 static_cast<std::underlying_type<Enum>::type>(enum),使用了类型特征函数 std::underlying_type

enum class Color { RED, GREEN, BLUE };
Color c = Color::RED;
std::underlying_type_t<Color> cu = std::to_underlying(c);

上面的代码将枚举类型 Color 的值 Color::RED 转换为其基础类型 std::underlying_type_t<Color>

std::swap

使用头文件 <utility> 中定义的函数 std::swap,您可以轻松交换两个对象。C++ 标准库中的通用实现内部使用函数 std::move

下面是带有移动语义的 std::swap 的示例实现代码:

#include <utility>

template <typename T>
inline void swap(T& a, T& b) noexcept {
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

上面的代码定义了一个模板函数 swap,可以交换两个类型为 T 的对象。该函数内部使用了 std::move 来实现移动语义,从而提高了效率

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/431447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【使用ChatGPT自动化】批量转换.xls文件为.xlsx文件

第1次提问&#xff1a; 我&#xff1a;我想使用Python批量转换.xls文件为.xlsx文件&#xff0c;请你提供代码 它&#xff1a; 当涉及到批量处理文件时&#xff0c;我们通常需要使用Python中的os模块和glob模块。os模块用于管理文件和目录&#xff0c;glob模块用于匹配文件路径名…

Visual Studio Code 1.77 发布!

欢迎使用 Visual Studio Code 2023 年3月版。此版本有许多更新&#xff0c;其中一些主要亮点包括&#xff1a; 无障碍改进&#xff1a;新的悬停、通知和 Sticky Scroll 快捷键 复制 GitHub 深度链接&#xff1a;在编辑器内创建永久链接和 HEAD 链接 笔记本保存格式&#xff1…

软件测试流程进阶----四年软件测试总结

工作四年了&#xff0c;我一直希望让自己每年对测试的理解更深入一层。工作一年的时候&#xff0c;我谈轮了自己对各种测试的理解&#xff0c;这一年来&#xff0c;虽然对那些理概念的有所加强&#xff0c;自我感觉没有什么质的变化。前些天听我们公司的一位测试经理讲《敏捷测…

精准抓住核心要点!!!十名面试官总结出这样一份面试通关答案,还不赶紧开始“作弊”通关!!!

金三银四求职季&#xff0c;但最近很多朋友私信说&#xff1a; 熬过了去年的寒冬&#xff0c;却没躲过如今的内卷&#xff1b; 打开Boss直拒&#xff0c;一排已读不回&#xff1b; 大部分回复的都是外包&#xff0c;薪资低于预期&#xff0c;对技术水平要求却远超从前&#x…

大数据和 CRM系统:它们如何帮助中小企业?

作为中小企业主&#xff0c;你可能在想&#xff0c;"大数据与我有什么关系&#xff1f;"但如果你使用某些类型的业务应用&#xff0c;即使预算很少&#xff0c;你也可以从大数据中获益。一个最好的例子是客户关系管理&#xff08;CRM&#xff09;系统&#xff0c;它提…

二极管反向恢复过程详细解析

二极管反向恢复过程&#xff0c;现代脉冲电路中大量使用晶体管或二极管作为开关, 或者使用主要是由它们构成的逻辑集成电路。而作为开关应用的二极管主要是利用了它的通(电阻很小)、断(电阻很大) 特性, 即二极管对正向及反向电流表现出的开关作用。二极管和一般开关的不同在于,…

在线帮助中心对企业的作用及解决方案

帮助中心对于一款互联网产品来说&#xff0c;重要性不言而喻。随着公司客户服务水平的不断提高&#xff0c;越来越多的公司逐渐重视客户服务。一个好的在线帮助中心必定能提高客户的转化率。那么&#xff0c;在线帮助中心对企业的帮助和作用有哪些呢? 在线帮助中心的作用 1.快…

3.10——常类型

常类型的引入&#xff0c;就是为了既保证数据共享又防止数据被改动。常类型是指使用类型修饰符const说明的类型&#xff0c;常类型的变量或对象成员的值在程序运行期间是不可改动的。 常引用 如果在说明引用时用const修饰&#xff0c;则被说明的引用为常引用。如果用常引用作为…

adb环境变量配置

adb环境变量配置Android一. 简介二. 环境变量配置1.JDK安装2.SDK安装3. 资源共享4. 配置环境变量4.1 方式一&#xff1a;4.2 方式二&#xff1a;5. adb常用命令的使用6. 结果Android List of ADB Commands and Fastboot Commands for Android 如果你是一个android用户&#xf…

paddleocr,windows pip 安装巨坑 lanms 库

安装 lanms 最佳参考&#xff1a; paddleocr&#xff0c;windows pip 安装巨坑 lanms 库 防丢失&#xff0c;直接抄录的一份 paddleocr最后几个库一个比一个难装&#xff0c;特别是 lanms 库&#xff0c;巨难装&#xff0c;拒绝任何花里胡哨&#xff0c;十分钟&#xff0c;三步…

HTML5 <menu> 标签

HTML5 <menu> 标签 实例 HTML5 <menu>标签用于定义菜单列表。 两个菜单按钮系列选项实例&#xff08;"File" 和 "Edit"&#xff09;&#xff1a; <menu type"toolbar"> <li> <menu label"File"> &…

Linux-初学者系列——篇幅4_系统运行相关命令

系统运行相关命令-目录一、关机重启注销命令1、重启或者关机命令-shutdown语法格式&#xff1a;常用参数&#xff1a;01 指定多久关闭/重启系统02 指定时间关闭/重启系统03 实现立即关闭/重启系统04 取消关闭/重启系统计划2、重启或者关机命令-halt/poweroff/reboot/systemctl语…

Lucene Solr Elasticsearch三者之间的关系,怎么选?

Lucene简介&#xff1a; Lucene主要用于构建文本搜索应用程序&#xff0c;包括Web搜索引擎、桌面搜索工具和商业应用程序。它提供了诸如单词分析、查询解析、搜索结果排序等功能&#xff0c;可以轻松地在大量文档中快速搜索和查找相关信息。 Lucene具有以下特点&#xff1a; …

Linux -- 进阶 Web 服务器 基础前瞻 ( 一 )

目录 WWW 简介 &#xff1a; 组件一个网站得需要哪些东西呢 &#xff1f;&#xff1f; 1 得有 响应用户申请网页服务 的软件 2 服务器主机 3 那就需要数据了 4. 浏览器 预备知识 &#xff1a; 网址及HTTP 简介 &#xff1a; URL : http 请求方法 &#xff1a;…

关于使用云渲染的五大优势

在不影响质量或性能的情况下节省时间、金钱和资源&#xff0c;对于需要在通常较短且严格的期限内创建高质量 3D 内容的专业人士来说&#xff0c;云渲染都是最好的选择&#xff01;云渲染作为数字媒体生产的最新趋势&#xff0c;与传统的渲染农场和机器相比具有许多优势&#xf…

每个程序员都应该知道的 5 个基本 Matlab 提示和技巧

当今软件开发和数据分析领域中&#xff0c;Matlab 是一种十分常用的工具。然而&#xff0c;对于许多程序员而言&#xff0c;它的高级特性可能并不为人所知。在本文中&#xff0c;我们将分享每个程序员都应该知道的 5 个基本 Matlab 提示和技巧&#xff0c;以帮助你更好地使用这…

【红队APT】反朔源流量加密CSMSF证书指纹C2项目CDN域前置

文章目录背景交代&#xff1a;常见红蓝对抗中红队面临问题&#xff1a;蓝队发现处置情况&#xff1a;NC-未加密&加密后-流量抓包对比MSF-流量通讯特征修改-证书-openssl解决HTTPS-SSL通讯证书被特征标示问题impersonate_ssl模块CS-流量通讯特征修改-证书指纹-keytool1.修改…

【C++】哈希的应用——bitset(STL)位图

哈希的应用——bitset(STL)位图 文章目录 哈希的应用——bitset(STL)位图一、bitset的介绍1.位图的引入2.位图的概念3.位图的应用 二、bitset的使用1.bitset的构造方式2.bitset成员函数的使用3.bitset运算符的使用 三、bitset位图的模拟实现1.位图的基本框架2.成员函数2.1.构造…

【Linux】进程学习(1)---理解进程概念

文章目录 冯诺依曼体系结构理解冯诺依曼体系结构 操作系统概念与定位概念计算机管理模型计算机的软硬件体系结构图系统调用和库函数概念 进程基本概念描述进程--PCBtask_struct内容分类组织进程 冯诺依曼体系结构 数学家冯诺依曼提出了计算机制造的三个基本原则&#xff0c;即采…

gnome换回纵向切换工作区

效果&#xff1a; 思路 最新的debian / ubuntu中用的gnome 4.x&#xff0c;工作区切换变成了左右切换&#xff0c;习惯了上下&#xff0c;真的很不舒服。 而且优化选项里也把设置开关取消掉了&#xff0c;解决方案是使用Vertical overview这个扩展&#xff1a; ## 安装扩展管…