C++23 std::bind_back:一种调用包装器 (P2387R3)
文章目录
- 引言
- 背景知识
- 旧有的绑定工具
- C++20的std::bind_front
- std::bind_back的定义和功能
- 定义
- 功能
- std::bind_back的使用场景
- 简化回调函数
- 部分应用参数
- std::bind_back与其他绑定工具的对比
- 与std::bind的对比
- 与std::bind_front的对比
- 总结
引言
在C++的发展历程中,每一个新标准的发布都会带来一些令人期待的新特性。C++23也不例外,其中std::bind_back
就是一个非常实用的新特性。它是一种调用包装器,为开发者在处理函数调用和参数绑定时提供了更多的灵活性。本文将详细介绍std::bind_back
的定义、功能、使用场景以及与其他相关工具的对比。
背景知识
在深入了解std::bind_back
之前,我们需要先了解一些相关的背景知识。在C++中,函数对象和可调用对象是非常重要的概念。函数对象是重载了()
运算符的类的实例,而可调用对象则是可以像函数一样被调用的对象,包括函数、函数指针、lambda表达式、bind对象等。
旧有的绑定工具
在C++11中引入了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::cout << "myDiv1(1000, 5) = " << myDiv1(1000, 5) << std::endl; // 200std::cout << "myDiv2(10) = " << myDiv2(10) << std::endl; // 200return 0;
}
在这个例子中,std::bind
将函数divMe
与不同的参数进行绑定,创建了新的可调用对象。
C++20的std::bind_front
C++20引入了std::bind_front
,它可以从可调用对象创建可调用包装器。调用std::bind_front(func, arg...)
会将所有参数arg
绑定到func
的前面,并返回一个可调用包装器。例如:
std::function<double(double)> myDiv3 = std::bind_front(divMe, 2000);
std::cout << "myDiv3(5) = " << myDiv3(5) << std::endl; // 400
这里std::bind_front
将参数2000
绑定到divMe
的前面,调用myDiv3(5)
时相当于调用divMe(2000, 5)
。
std::bind_back的定义和功能
定义
std::bind_back
是C++23中引入的一个函数模板,在标头<functional>
中定义。其原型如下:
template < class F, class ... Args >
constexpr /*unspecified*/ bind_back( F&& f, Args&&... args );
它为f
生成转发调用包装。调用此包装等价于绑定尾sizeof...(Args)
个参数到args
再调用f
。换言之,std::bind_back (f, bound_args...) (call_args...)
等价于 std::invoke( f, call_args..., bound_args...)
。
功能
std::bind_back
的主要功能是将参数绑定到可调用对象的后面,创建一个新的可调用包装器。这使得在某些场景下,我们可以更方便地处理函数调用和参数传递。例如:
std::function<double(double)> myDiv4 = std::bind_back(divMe, 10);
std::cout << "myDiv4(2000) = " << myDiv4(2000) << std::endl; // 200
在这个例子中,std::bind_back
将参数10
绑定到divMe
的后面,调用myDiv4(2000)
时相当于调用divMe(2000, 10)
。
std::bind_back的使用场景
简化回调函数
在很多异步编程场景下,常常需要传递一个不带参数的回调函数。std::bind_back
可以让你将带参数的函数转换为无参数的形式,非常适合线程池、std::function<void()>
回调等场景。例如:
#include <iostream>
#include <thread>
#include <functional>void task(int x, int y) {std::cout << "Task with values: " << x << " and " << y << std::endl;
}int main() {auto bound_task = std::bind_back(task, 42);std::thread t([&]() { bound_task(10); });t.join();return 0;
}
在这个例子中,std::bind_back
将参数42
绑定到task
的后面,创建了一个新的可调用对象bound_task
。然后将其封装在lambda表达式中传递给线程,调用时只需要提供剩余的参数10
。
部分应用参数
std::bind_back
主要用于部分应用函数的参数。也就是说,您可以用std::bind_back
预先绑定函数的一部分参数,然后在之后的调用中只提供其余参数。这使得代码更加灵活和模块化,尤其是当需要在不同的上下文中使用相同的函数但具有不同的部分参数时。例如:
#include <iostream>
#include <functional>int add(int a, int b, int c) {return a + b + c;
}int main() {auto partial_add = std::bind_back(add, 3, 4);std::cout << partial_add(2) << std::endl; // 9return 0;
}
在这个例子中,std::bind_back
将参数3
和4
绑定到add
的后面,创建了一个新的可调用对象partial_add
。调用partial_add(2)
时相当于调用add(2, 3, 4)
。
std::bind_back与其他绑定工具的对比
与std::bind的对比
- 灵活性:
std::bind
可以将参数绑定到任意位置,还可以改变参数的顺序,引入占位符等。而std::bind_back
只能将参数绑定到可调用对象的后面。 - 可读性:
std::bind
的使用可能会导致代码变得晦涩难懂,尤其是在使用占位符时。而std::bind_back
的语义更加明确,代码可读性更高。
与std::bind_front的对比
- 绑定位置:
std::bind_front
将参数绑定到可调用对象的前面,而std::bind_back
将参数绑定到可调用对象的后面。这使得它们在不同的场景下有不同的用途。 - 使用场景:当需要将参数绑定到前面时,使用
std::bind_front
;当需要将参数绑定到后面时,使用std::bind_back
。
总结
std::bind_back
是C++23中一个非常实用的新特性,它为开发者在处理函数调用和参数绑定时提供了更多的灵活性。通过将参数绑定到可调用对象的后面,std::bind_back
可以简化回调函数、部分应用参数等。与std::bind
和std::bind_front
相比,它具有自己独特的优势和适用场景。在实际开发中,我们可以根据具体的需求选择合适的绑定工具。