std::function 是一种通用的模板类,用于封装不同类型的可调用对象并通过统一的接口调用它们,使得处理这些可调用对象变得更加简洁、灵活。


一、什么是 std::function

std::function 是 C++ 标准库中的一个通用包装器,用于存储、传递和调用任何可调用对象。无论是函数、Lambda 表达式、函数对象(仿函数)还是成员函数,都可以通过 std::function 进行包装和调用。它的主要特性包括:

  • 通用性:能够封装不同类型的可调用对象。
  • 一致性:通过统一的接口调用不同类型的可调用对象。
  • 类型安全:通过模板机制,保证调用时的类型安全。

std::function 的原型如下:

1
2
template<class _Fty>
class function;
  • _Fty 是可调用对象的类型,通常格式为:返回类型(参数列表)

为了使用 std::function,你需要包含头文件:

1
#include <functional>

二、std::function 的基本用法

std::function 的使用非常简单,它可以包裹不同类型的可调用对象。下面我们看一看如何将普通函数、静态成员函数、仿函数、Lambda 表达式以及类的成员函数都包装进 std::function 对象中,并进行调用。

2.1 包装普通函数

普通函数是最常见的可调用对象。我们可以用 std::function 来包装普通函数,并通过包装器对象进行调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <iostream>
#include <functional>
using namespace std;

// 普通函数
void show(int bh, const string& message) {
    cout << "亲爱的 " << bh << "," << message << endl;
}

int main() {
    // 使用 std::function 包装普通函数
    std::function<void(int, const string&)> fn = show;

    // 通过 std::function 调用普通函数
    fn(1, "我是一只傻傻鸟。");
    return 0;
}

2.2 包装类的静态成员函数

类的静态成员函数本质上与普通函数类似,因此也可以通过 std::function 进行包装。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct AA {
    static void show(int bh, const string& message) {
        cout << "亲爱的 " << bh << "," << message << endl;
    }
};

int main() {
    // 使用 std::function 包装静态成员函数
    std::function<void(int, const string&)> fn = AA::show;

    // 通过 std::function 调用静态成员函数
    fn(2, "我是一只傻傻鸟。");
    return 0;
}

2.3 包装仿函数(函数对象)

仿函数是通过重载 operator() 操作符的类对象。它们在需要自定义行为时非常有用,也可以通过 std::function 进行包装。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct BB {
    void operator()(int bh, const string& message) {
        cout << "亲爱的 " << bh << "," << message << endl;
    }
};

int main() {
    // 包装仿函数
    std::function<void(int, const string&)> fn = BB();

    // 通过 std::function 调用仿函数
    fn(3, "我是一只傻傻鸟。");
    return 0;
}

2.4 包装 Lambda 表达式

Lambda 表达式是 C++11 引入的一种简洁的函数定义方式,特别适合用于短小的函数逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main() {
    // 定义 Lambda 表达式
    auto lambda = [](int bh, const string& message) {
        cout << "亲爱的 " << bh << "," << message << endl;
    };

    // 使用 std::function 包装 Lambda 表达式
    std::function<void(int, const string&)> fn = lambda;

    // 通过 std::function 调用 Lambda 表达式
    fn(4, "我是一只傻傻鸟。");
    return 0;
}

2.5 包装类的非静态成员函数

包装非静态成员函数略显复杂,因为非静态成员函数只能通过对象来调用。因此,std::function 在包装非静态成员函数时,必须将对象和成员函数一起传递。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct CC {
    void show(int bh, const string& message) {
        cout << "亲爱的 " << bh << "," << message << endl;
    }
};

int main() {
    CC cc;

    // 使用 std::function 包装非静态成员函数
    std::function<void(CC&, int, const string&)> fn = &CC::show;

    // 通过 std::function 调用非静态成员函数
    fn(cc, 5, "我是一只傻傻鸟。");
    return 0;
}

2.6 包装可转换为函数指针的类对象

如果一个类可以通过类型转换运算符转换为函数指针,它的对象也可以被 std::function 包装。同样的,这个方面仅作了解吧。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct DD {
    using Fun = void (*)(int, const string&);
    operator Fun() {
        return show;  // 转换为普通函数指针
    }
};

int main() {
    DD dd;

    // 使用 std::function 包装可转换为函数指针的对象
    std::function<void(int, const string&)> fn = dd;

    // 通过 std::function 调用普通函数
    fn(6, "我是一只傻傻鸟。");
    return 0;
}

三、std::function 的异常处理

std::function 未包装任何可调用对象时,使用它将抛出 std::bad_function_call 异常。我们可以通过重载的 bool 运算符来检测 std::function 是否有效。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int main() {
    std::function<void(int, const string&)> fn;

    try {
        if (fn) {
            fn(7, "这是一个有效的调用。");
        } else {
            cout << "未包装任何可调用对象。" << endl;
        }
    }
    catch (std::bad_function_call& e) {
        cout << "抛出了 std::bad_function_call 异常。" << endl;
    }

    return 0;
}

四、总结

std::function 是一个强大的工具,能够统一处理各种类型的可调用对象。无论是普通函数、静态成员函数、仿函数、Lambda 表达式,还是类的非静态成员函数,std::function 都能通过统一的接口进行管理和调用,从而简化了 C++ 编程中的函数对象管理。