需要预先了解:


1. 函数指针

1.1 函数的地址

函数的机器指令存储在内存的.text段里,每个函数对应.text段中的一段连续的机器指令序列,其起始地址(即函数第一条指令所在的内存地址)就是这个函数的地址。


1.2 函数指针

函数指针是指向函数的指针类型,它存储函数的入口地址,通过这个地址可以间接调用对应的函数。

其声明语法为返回值类型 (*函数指针名)(参数列表):

int add(int a, int b);
int (*p)(int, int);   // p 是类型为 int (*)(int, int)的函数的指针

函数名也可以隐式转换为函数的入口地址

因此有以下两种等价的写法:

int (*p)(int, int) = add;
int (*p)(int, int) = &add;

调用函数指针也有等价的两种写法:

std::cout << p(1, 2) << std::endl;
std::cout << (*p)(1, 2) << std::endl;

2. std::function

std::function定义在头文件<functional>,是通用可调用对象的包装器,可以统一存储、传递、调用任何可调用类型,包括:

  • 普通的全局函数
  • 类成员函数/静态成员函数
  • Lambda表达式
  • 函数指针
  • 重载operator()的仿函数

其语法模板为:

template<class R, class... Args>
class function<R(Args...)>;

2.1 具体操作

假定有如下定义:

#include <functional>
#include <iostream>

void foo()
{
    std::cout << "foo called" << std::endl;
}

int add(int x, int y)
{
    return x + y;
}

1. 构造函数:可以从函数指针、函数对象、Lambda表达式等创建std::function对象

// 创建一个 std::function 对象,指向 foo
std::function<void()> f1 = foo;
f1();    // 调用 f1 指向的对象,即 foo

// 创建一个 std::function 对象,指向 add
std::function<int(int, int)> f2 = add;
std::cout << f2(1, 2) << std::endl;

// 创建一个 std::function 对象,指向一个 Lambda 表达式
std::function(void()) f3 = [] { std::cout <<"this is Lambda!" << std::endl; }
f3();

2. 空检查:用bool operator() const或bool operator bool() const判断std::function对象是否为空

std::function<void()> func = nullptr;

if(func)
{
    std::cout << "func 不为空" << std::endl;
}
else
{
    std::cout << "func 是空的" << std::endl;
}

3. 封装仿函数:仿函数即重载operator ()的类,可以像函数一样对其进行调用

struct A
{
    void operator()(double x)
    {
        std::cout << x << std::endl;
    }
};

A A_instance;
std::function<void(double)> func = A_instance;
func(3.14);

3. std::bind

std::bind是参数绑定器,接收一个可调用对象(函数、成员函数、lambda表达式、仿函数、函数指针)以及参数,返回一个新的可调用对象(类型为函数对应的std::functiom)。其定义在头文件<functional>里。 其语法为:

auto new_func = std::bind(可调用对象, 绑定参数列表);

3.1 占位符 std::placeholders

std::placeholders::_1、std::placeholders::_2、……、std::placeholders::_N依次表示新函数调用时候传入的第1个实参、第2个实参、……、第N个实参。


3.2 用途

1. 用std::bind固定部分参数:

int sum(int x, int y, int z) { return x + y + z; }

std::function<int(int, int)> f = 
std::bind(sum, 10, std::placeholders::_1, std::placeholders::_2);
std::cout << f(1, 2) << std::endl;

2. 用std::bind打乱参数顺序:

int sub(int a, int b) { return a - b; }

auto f = std::bind(sub, std::placeholders::_2, std::placeholders::_1);
f(10, 3);

3. 用std::bind固定类的成员函数:类的成员函数隐含第一个参数是this指针的含义,因此需按照std::bind(&类::成员函数,对象的地址,其余参数/占位符)的方式进行传入:

struct student
{
    void print() { std::cout << "I'm a student" << std::endl; }
}

Student s;
auto func = std::bind(&Student::print, &s);

注: 静态成员函数没有this指针,用法和普通全局函数一致,不需要传对象。