std::function 用法详解

std::function 是 C++11 引入的通用函数包装器(定义于 <functional> 头文件),可存储、复制和调用任何可调用对象(函数、lambda 表达式、函数指针、仿函数、绑定表达式等),是回调机制、事件处理等场景的核心工具。

基本用法:定义与调用

std::function 的模板参数为函数签名(返回类型 + 参数列表),格式为:


std::function<返回类型(参数类型1, 参数类型2, ...)>

示例:包装不同类型的可调用对象


#include 
<functional>
#include 
<iostream>

// 1. 普通函数
int add(int a, int b)
{
    return a + b;
}

// 2. 仿函数(重载 operator() 的结构体)
struct Multiply
{
    int operator()(int a, int b) { return a * b; }
};

int main()
{
    // 包装普通函数
    std::function<int(int, int)> func1 = add;

    // 输出:5
    std::cout << func1(2, 3) << std::endl;

    // 包装仿函数
    std::function<int(int, int)> func2 = Multiply{};

    // 输出:6
    std::cout << func2(2, 3) << std::endl; 

    // 包装 lambda 表达式
    std::function<int(int, int)> func3 = [](int a, int b) { return a - b; };

    // 输出:3
    std::cout << func3(5, 2) << std::endl; 

    // 包装函数指针
    int (*sub_ptr)(int, int) = [](int a, int b) { return a - b; };

    // lambda 转换为函数指针(无捕获时)
    std::function<int(int, int)> func4 = sub_ptr;

    // 输出:2
    std::cout << func4(5, 3) << std::endl;

    return 0;
}

核心特性:存储与传递可调用对象

std::function 的核心价值是统一不同可调用对象的类型,便于存储和传递(如作为函数参数、返回值、容器元素)。

1. 作为函数参数(回调函数场景)


// 接收 std::function 作为参数,实现通用回调
void process(int a, int b, std::function<int(int, int)> op) 
{
    std::cout << "结果: " << op(a, b) << std::endl;
}

int main() 
{

    // 普通函数作为回调
    process(10, 5, add);       

    // 仿函数作为回调
    process(10, 5, Multiply{});      

    // lambda 作为回调
    process(10, 5, [](int a, int b) { return a / b; }); 
    return 0;
}

2. 作为函数返回值


// 根据条件返回不同的运算函数
std::function<int(int, int)> GetOperator(char op)
{
    switch (op)
    {
    case '+':
        return add;

    case '*':
        return Multiply{};

    case '-':
        return [](int a, int b) { return a - b; };

    default:
        return [](int, int) { return 0; };
    }
}

int main() 
{
    auto op = GetOperator('*');

    // 输出:12
    std::cout << op(3, 4) << std::endl;  
    return 0;
}

3. 作为容器元素


#include 
<vector>

int main() 
{
    std::vector<std::function<int(int)>> funcs;

    // 乘以 2
    funcs.push_back([](int x) { return x * 2; });   

    // 加 10
    funcs.push_back([](int x) { return x + 10; });  

    // 平方
    funcs.push_back([](int x) { return x * x; });   

    int x = 3;
    for (auto& f : funcs) 
    {
        std::cout << f(x) << " ";  // 输出:6 13 9
    }

    return 0;
}

3. 结合 std::bind 绑定参数

std::function配合std::bind,固定部分参数,返回新的可调用对象。


#include 
<functional>

// 原函数:3个参数
int sum(int a, int b, int c)
{
    return a + b + c;
}

int main() 
{
    // 绑定前两个参数为 10 和 20,仅保留最后一个参数
    // std::placeholders::_n 表示“第n个未绑定的参数”,调用时需传入。
    auto bindFunc = std::bind(sum, 10, 20, std::placeholders::_1);

    // 用 std::function 包装绑定后的函数(此时只需 1 个参数)
    std::function<int(int)> func = bindFunc;

    // 10 + 20 + 30 = 60
    std::cout << func(30) << std::endl;  
    return 0;
}

4. 绑定成员函数

std::function 可通过绑定具体对象或指针包装类的成员函数。


#include 
<functional>
#include 
<iostream>

class Math
{
public:
    int pow(int a, int b)
    {
        // 成员函数
        int res = 1;
        for (int i = 0; i < b; ++i)
        {
            res *= a;
        }

        return res;
    }
};

int main()
{
    Math m;

    // 包装成员函数:需传入对象指针 + 成员函数地址
    // 成员函数的第一个隐含参数是 this指针,因此绑定需显式传入对象(或指针)。
    std::function<int(int, int)> func = std::bind(&Math::pow, &m,
                                                  std::placeholders::_1,
                                                  std::placeholders::_2);
    // 2^3 = 8
    std::cout << func(2, 3) << std::endl; 
    return 0;
}

总结

1. 主要用法

std::function是C++中统一可调用对象的“万能容器”,主要用法包括:

  • 包装函数、lambda、仿函数等可调用对象;
  • 作为参数传递(回调函数)、作为返回值(函数工厂)、作为容器元素;
  • 结合 std::bind 处理参数绑定和成员函数;
  • 需注意空状态检查和对象生命周期。

2. 注意事项

  1. 性能开销std::function 内部通过类型擦除实现,调用时会有轻微的性能损耗(比直接调用函数指针略慢),但通常可忽略。
  2. 生命周期管理:若包装的是捕获了引用的 lambda 或绑定了局部对象的 std::bind 表达式,需确保引用的对象生命周期长于 std::function,否则会导致未定义行为。
  3. 不可拷贝的可调用对象std::function 要求存储的可调用对象是可拷贝的,若对象不可拷贝(如 std::unique_ptr 捕获到 lambda 中),则无法存储(C++17 起可部分支持移动语义)。
  4. 与 lambda 的配合:无捕获的 lambda 可隐式转换为函数指针,而有捕获的 lambda 只能通过 std::function 包装。
发表在 C/C++ | 留下评论