工具库
functional
std::bind
std::bind
接受一个可调用对象,生成一个新的可调用对象来适应原可调用对象的参数列表。
std::bind
将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function
保存。std::bind
主要有以下两个作用:
- 将可调用对象和其参数绑定成一个仿函数;
- 只绑定部分参数,减少可调用对象传入的参数;
原型
// 普通函数
template< class F, class... Args >
/*unspecified*/ bind( F&& f, Args&&... args );
// 成员函数
template< class R, class F, class... Args >
/*unspecified*/ bind( F&& f, Args&&... args );
std::bind绑定普通函数
double callableFunc (double x, double y)
{
return x / y;
}
auto NewCallable = std::bind (callableFunc, std::placeholders::_1, 2);
std::cout << NewCallable (10) << std::endl;
std::bind
的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind(callableFunc,_1,2)
等价于std::bind (&callableFunc,_1,2)
- 预绑定的参数是以值传递的形式,不预绑定的参数要用
std::placeholders
(占位符)的形式占位,从_1开始,依次递增,是以引用传递的形式 std::placeholders::_1
表示占位符,第一个参数被占位符占用,表示这个参数以调用时传入的参数为准,在这里调用NewCallable(10)
时,其实就等价于调用callableFunc(10,2)
std::bind绑定成员函数
class Base {
public:
void display_sum(int a1, int a2)
{
std::cout << a1 + a2 << std::endl;
}
int m_data = 30;
};
int main()
{
Base base;
auto newFunc = std::bind(&Base::display_sum, &base, 100, std::placeholders::_1);
// should output 120
newFunc(20);
}
std::bind
绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址,第三个及以后的参数(如果有)表示该成员函数的入参- 必须显式地指定
&Base::diplay_sum
,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Base::display_sum
前添加&
std::ref
std::ref
函数接受一个对象作为参数,并返回一个引用包装器。引用包装器是一个类模板std::reference_wrapper
的实例,它主要将一个对象转换成一个引用类型,并提供了访问该对象的引用的方法。需要注意的是,std::reference_wrapper并不是一个裸引用,它本身是一个对象,可以被复制和赋值。原型
template< class T >
std::reference_wrapper <T> ref( T& t ) noexcept;
template< class T >
std::reference_wrapper
ref( std::reference_wrapper<T> t ) noexcept;
template< class T >
void ref( const T&& ) = delete;
用来构建一个std::reference_wrapper
对象并返回,该对象拥有传入的t
变量的引用。
如果参数本身是一个std::reference_wrapper
类型的对象,则创建该对象的一个副本并返回。
用法
std::ref
一般与std::bind
搭配使用,std::bind
是对参数直接拷贝,无法传入引用(即使传入的实参是引用类型也不行),故引入std::ref()
。使用std::ref
可以在模板传参的时候传入引用。
std::ref
能使用std::reference_wrapper
包装好的引用对象代替原本会被识别的值类型,而std::reference_wrapper
能隐式转换为被引用的值的引用类型。
在使用std::thread
进行多线程编程时,也会发生这样的问题,std::thread
的线程函数传递引用的时候,必须用std::ref
来进行引用传递,否则就是浅拷贝。
std::ref
只是尝试模拟引用传递,并不能真正变成引用,在非模板情况下,std::ref
根本没法实现引用传递,只有模板自动推导类型或类型隐式转换时,std::ref
能用包装类型std::reference_wrapper
来代替原本会被识别的值类型,而std::reference_wrapper
能隐式转换为被引用的值的引用类型。
#include <iostream>
#include <functional>
void Func(int& a, int& b)
{
std::cout << "Func: a = " << a << " b = " << b << std::endl;
std::cout << "Func: &a = " << &a << " &b = " << &b << std::endl;
a++;
b++;
}
int main()
{
int a = 1, b = 10;
int& ra = a;
// 第一个参数即便是引用类型,std::bind传入的还是其值的拷贝
// 第二个参数传入std::reference_wrapper对象,可隐式的转换为值的引用
std::function<void()> newFunc = std::bind(Func, ra, std::ref(b));
newFunc();
std::cout << "Main: a = " << a << " b = " << b << std::endl;
std::cout << "Main: &a = " << &a << " b = " << &b << std::endl;
newFunc();
std::cout << "Main: a = " << a << " b = " << b << std::endl;
std::cout << "Main: &a = " << &a << " b = " << &b << std::endl;
}
output:
Func: a = 1 b = 10
Func: &a = 0x5bdb5531e2c0 &b = 0x7ffd2bc8118c
Main: a = 1 b = 11
Main: &a = 0x7ffd2bc81188 b = 0x7ffd2bc8118c
Func: a = 2 b = 11
Func: &a = 0x5bdb5531e2c0 &b = 0x7ffd2bc8118c
Main: a = 1 b = 12
Main: &a = 0x7ffd2bc81188 b = 0x7ffd2bc8118c