std::function
是一种通用的模板类,用于封装不同类型的可调用对象并通过统一的接口调用它们,使得处理这些可调用对象变得更加简洁、灵活。
一、什么是 std::function
?#
std::function
是 C++ 标准库中的一个通用包装器,用于存储、传递和调用任何可调用对象。无论是函数、Lambda 表达式、函数对象(仿函数)还是成员函数,都可以通过 std::function
进行包装和调用。它的主要特性包括:
- 通用性:能够封装不同类型的可调用对象。
- 一致性:通过统一的接口调用不同类型的可调用对象。
- 类型安全:通过模板机制,保证调用时的类型安全。
std::function
的原型如下:
1
2
|
template<class _Fty>
class function;
|
_Fty
是可调用对象的类型,通常格式为:返回类型(参数列表)
。
为了使用 std::function
,你需要包含头文件:
二、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++ 编程中的函数对象管理。