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

utility

std::move

std::forward