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

C++ 23 实用工具(二)绑定工具

C++ 23 实用工具(二)绑定工具

267404

Adaptors for Functions

std::bindstd::bind_frontstd::bind_backstd::function这四个函数非常适合一起使用。

其中,std::bindstd::bind_frontstd::bind_back可以让您即时创建新的函数对象,而std::function则可以将这些临时的函数对象绑定到变量上。

然而,在C++中,这四个函数大多数情况下都是多余的。首先,您可以使用lambda表达式代替std::bindstd::bind_frontstd::bind_back;其次,您通常可以使用自动类型推导的auto关键字代替std::function

因此,尽管在某些特定场景下,这些函数仍然是有用的,但在大多数情况下,它们并不是必须的。

std::bind

#include <functional>
#include <iostream>

using namespace std::placeholders;

double divMe(double a, double b) { return a / b; };

int main()
{
	std::function<double(double, double)> myDiv1 = std::bind(divMe, _1, _2);
	std::function<double(double)> myDiv2 = std::bind(divMe, 2000, _1);
	std::function<double(double)> myDiv3 = std::bind_front(divMe, 2000);
	std::function<double(double)> myDiv4 = std::bind_back(divMe, 10);

	std::cout << "myDiv1(1000, 5) = " << myDiv1(1000, 5) << std::endl; // 200
	std::cout << "myDiv2(10) = " << myDiv2(10) << std::endl; // 200
	std::cout << "myDiv3(5) = " << myDiv3(5) << std::endl; // 400
	std::cout << "myDiv4(2000) = " << myDiv4(2000) << std::endl; // 200
	return 0;
}

借助std::bind,您可以以多种方式创建函数对象:

  • 将参数绑定到任意位置
  • 改变参数的顺序
  • 引入占位符
  • 部分求值函数

通过std::bind创建的新函数对象可以被调用、用于STL算法或者存储在std::function中。

std::bind_front (C++20)

std::bind_front函数可以从可调用对象创建可调用包装器。调用std::bind_front(func, arg...)会将所有参数arg绑定到func的前面,并返回一个可调用包装器。

std::bind_back (C++23)

std::bind_back函数可以从可调用对象创建可调用包装器。调用std::bind_back(func, arg...)会将所有参数arg绑定到func的后面,并返回一个可调用包装器。

std::function

std::function函数可以将任意可调用对象存储到变量中,它是一个多态的函数包装器。可调用对象可以是lambda函数、函数对象或者函数。如果需要显式指定可调用对象的类型,则必须使用std::function,它无法被auto关键字替换。

std::tie 和 std::ignore

std::tie函数可以创建引用变量的元组。当您需要同时返回多个值时,可以使用std::tie函数将这些值打包成一个元组返回,并通过引用将元组的内容解包到变量中。

例如,假设您有一个返回两个值的函数foo(),您可以使用std::tie函数将这两个值打包成一个元组返回,并通过引用将元组的内容解包到两个变量中:

int x, y;
std::tie(x, y) = foo();

如果您不需要元组中的某个元素,则可以使用std::ignore函数将其忽略。例如,假设您只需要元组中的第一个值,可以将第二个值用std::ignore函数忽略:

int x, y;
std::tie(x, std::ignore) = foo();

这样,foo()返回的元组中的第二个值将被忽略。

#include <tuple>
#include <iostream>

using namespace std;

int main()
{
	int first = 1;
	int second = 2;
	int third = 3;
	int fourth = 4;
	cout << first << " " << second << " " << third << " " << fourth << endl; // 1 2 3 4

	auto tup = std::tie(first, second, third, fourth) = std::make_tuple(101, 102, 103, 104); // 绑定元组并赋值
	cout << get<0>(tup) << " " << get<1>(tup) << " " << get<2>(tup) << " " << get<3>(tup) << endl; // 101 102 103 104
	cout << first << " " << second << " " << third << " " << fourth << endl; // 101 102 103 104

	first = 201;
	get<1>(tup) = 202;
	cout << get<0>(tup) << " " << get<1>(tup) << " " << get<2>(tup) << " " << get<3>(tup) << endl; // 201 202 103 104
	cout << first << " " << second << " " << third << " " << fourth << endl; // 201 202 103 104

	int a, b;
	tie(std::ignore, a, std::ignore, b) = tup;
	cout << a << " " << b << endl; // 202 104

	return 0;
}

Reference Wrappers

Reference Wrappers是一个定义在头文件中的可复制构造和可复制赋值的包装器,用于类型&的对象。它具有像引用一样的行为,但可以被复制。与传统引用不同,std::reference_wrapper对象支持两个附加用例:

  • 您可以在标准模板库的容器中使用它们。例如:std::vector<std::reference_wrapper<int>> myIntRefVector
  • 您可以复制具有std::reference_wrapper对象的类的实例。通常情况下,这对于引用是不可能的。

get成员函数允许访问引用:myInt.get()。您可以使用引用包装器来封装和调用可调用对象。

Reference Wrappers在处理需要使用引用的情况时非常有用,同时也允许在STL容器中存储引用类型的对象,这是传统引用无法做到的。因此,它是C++中一个非常方便的工具。

#include <functional>
#include <iostream>

void foo()
{
	std::cout << "被调用了" << '\n';
}

typedef void callableUnit();
std::reference_wrapper<callableUnit> refWrap(foo);

int main()
{
	refWrap(); // 输出 "被调用了"
	return 0;
}

这段代码定义了一个名为foo的函数,它没有参数和返回值。接下来,我们定义了一个名为callableUnit的函数类型别名,它代表没有参数和返回值的函数类型。然后,我们使用std::reference_wrapperfoo包装到refWrap中。

main函数中,我们直接调用refWrap(),这实际上会调用foo()函数并输出"被调用了"。由于refWrap是一个std::reference_wrapper对象,它可以像函数一样被调用。


http://www.kler.cn/a/10348.html

相关文章:

  • 贪心算法入门(二)
  • 网络安全-蓝队基础
  • qt QVideoWidget详解
  • Unity3D 包体裁剪与优化详解
  • vue el-date-picker 日期选择器禁用失效问题
  • 工业相机选取
  • 10.字符串
  • 学习机器人SLAM导航核心技术(一)
  • 网络之广播域和冲突域
  • Qt5.12实战之规则DLL导出函数使用
  • 什么是分布式锁
  • 软考第二章 数据通信基础
  • 写博客8年与人生第一个502万
  • 空间复杂度
  • Makefile第十课:Makefile编译
  • 当你觉得生活快熬不下去时,请你读一读《活着》
  • Elastic Enterprise Search 8.7:新连接器、网络爬虫提取规则和搜索分析客户端测试版
  • Golang数据类型比较
  • MongoDB
  • 使用 ArcGIS Pro 进行土地利用分类的机器学习和深度学习
  • SpringBoot常见的的面试点
  • ArrayList、LinkedList与Vector的区别?
  • 【自用】HTML笔记
  • VS Code 快捷键
  • 【C++11那些事儿(一)】
  • pandas读取Excel核心源码剖析,面向过程仿openpyxl源码实现Excel数据加载