std::function
存储任意可调用对象
std::function 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。
什么是 std::function
std::function 是一种类型擦除包装器,可以容纳与给定签名匹配的任何可调用对象:普通函数、lambda 表达式、函子或绑定表达式。
- 使用目标签名声明。
- 位于
<functional>中。
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> f = add;
std::cout << f(2, 3) << '\n';
return 0;
}存储 lambda 表达式
与原始函数指针不同,std::function 可以轻松存储 lambda 表达式,即使该表达式带有捕获也没问题。
#include <iostream>
#include <functional>
int main() {
int offset = 10;
std::function<int(int)> f = [offset](int x) { return x + offset; };
std::cout << f(5) << '\n';
return 0;
}存储函子
函子是一个包含 operator() 的对象。std::function 可以包装函子,并在被调用时调用它的运算符。
#include <iostream>
#include <functional>
struct Multiplier {
int factor;
int operator()(int x) const { return x * factor; }
};
int main() {
std::function<int(int)> f = Multiplier{3};
std::cout << f(4) << '\n';
return 0;
}重新分配目标
在其生命周期内,一个 std::function 变量可以指向不同的可调用对象,这对于在运行时切换行为很有用。
#include <iostream>
#include <functional>
int main() {
std::function<int(int)> f = [](int x) { return x + 1; };
std::cout << f(10) << '\n';
f = [](int x) { return x * x; };
std::cout << f(10) << '\n';
return 0;
}空的 std::function
默认构造的 std::function 不包含任何内容。调用它会抛出 std::bad_function_call,因此请先进行测试。
#include <iostream>
#include <functional>
int main() {
std::function<void()> f;
if (f) {
f();
} else {
std::cout << "empty\n";
}
return 0;
}作为参数
函数可以接收 std::function 参数,以接受任何匹配的可调用对象,从而使应用程序接口更加灵活。
#include <iostream>
#include <functional>
void repeat(int n, const std::function<void(int)>& action) {
for (int i = 0; i < n; ++i) action(i);
}
int main() {
repeat(3, [](int i) { std::cout << "step " << i << '\n'; });
return 0;
}存储回调函数
一种常见用法是将回调函数保存在类成员中,以便事件发生时稍后调用它。
#include <iostream>
#include <functional>
struct Button {
std::function<void()> onClick;
void click() { if (onClick) onClick(); }
};
int main() {
Button b;
b.onClick = [] { std::cout << "clicked!\n"; };
b.click();
return 0;
}可调用对象的向量
由于它们共享同一种类型,不同的可调用对象可以共同存放在容器中,并依次被调用。
#include <iostream>
#include <functional>
#include <vector>
int inc(int x) { return x + 1; }
int main() {
std::vector<std::function<int(int)>> ops;
ops.push_back(inc);
ops.push_back([](int x) { return x * 2; });
for (auto& op : ops) std::cout << op(10) << ' ';
std::cout << '\n';
return 0;
}类型擦除的代价
std::function 可能会分配内存,并会增加一次间接调用,因此它的速度略慢于直接调用或使用模板的可调用对象。需要统一存储时再使用它。
#include <iostream>
#include <functional>
int main() {
std::function<int()> f = [] { return 42; };
int total = 0;
for (int i = 0; i < 5; ++i) total += f();
std::cout << total << '\n';
return 0;
}返回 std::function
工厂函数可以构建并返回一个经过配置的可调用对象,让调用者获得自定义行为。
#include <iostream>
#include <functional>
std::function<int(int)> adder(int n) {
return [n](int x) { return x + n; };
}
int main() {
auto add5 = adder(5);
std::cout << add5(10) << '\n';
return 0;
}与函数指针对比
std::function 比函数指针更灵活,因为它可以存储状态,但这种灵活性会带来少量性能开销,并且可能发生内存分配。
#include <iostream>
#include <functional>
int main() {
int base = 7;
std::function<int(int)> stateful = [base](int x) { return x + base; };
std::cout << "Stateful result: " << stateful(3) << '\n';
return 0;
}快速检查
测试您对 std::function 的理解。
回顾
您了解到 std::function:
- 包装任何与其签名匹配的可调用对象,包括有状态的 lambda 表达式和函子
- 可以重新赋值、存储在容器中,以及从工厂函数返回
- 为空时会抛出
bad_function_call - 会因类型擦除产生少量额外开销
接下来,您将使用 std::bind 预先填充可调用对象的参数。
常见问题解答
「std::function」课时是免费的吗?
是的 — 「std::function」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。
「std::function」这节课中我会学到什么?
存储任意可调用对象 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C++ Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「std::function」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C++ Academy 课中编写并运行代码吗?
能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。