2023年11月12日

C++11新特性:std::function详解


std::function 是 C++11 引入的一个非常强大的标准库工具,专门用于存储和调用任意类型的可调用对象(如函数、Lambda 表达式、函数对象等)。本文将带你深入了解 std::function 的用途、实现原理及其在实际开发中的应用。

1. 什么是 std::function

std::function 是一个模板类,用于封装任意可调用对象,使其可以以统一的方式进行存储和调用。它主要用于需要传递、存储或返回函数对象的场合。

例如,下面的代码展示了如何将普通函数和 Lambda 表达式赋值给 std::function

std::function<int(int, int)> func = add;func = [](int a, int b) { return a + b; };

2. std::function 的基本用法

1. 普通函数

int add(int a, int b) { return a + b;}std::function<int(int, int)> func = add;std::cout << func(2, 3) << std::endl; // 输出 5

2. Lambda 表达式

std::function<int(int, int)> func = [](int a, int b) { return a + b;};std::cout << func(2, 3) << std::endl; // 输出 5

3. 函数对象

struct Adder { int operator()(int a, int b) const { return a + b; }};std::function<int(int, int)> func = Adder();std::cout << func(2, 3) << std::endl; // 输出 5

4. 成员函数

struct Calculator { int multiply(int a, int b) const { return a * b; }};Calculator calc;std::function<int(int, int)> func = std::bind(&Calculator::multiply, calc, std::placeholders::_1, std::placeholders::_2);std::cout << func(2, 3) << std::endl; // 输出 6

3. std::function 的内部实现

#include <iostream>
#include <memory>
#include <utility>
template <typename>
class function; // Forward declaration
template <typename R, typename... Args>
class function<R(Args...)> {
public:
function() noexcept : callable(nullptr) {}
template <typename F>
function(F f) : callable(new CallableType<F>(std::move(f))) {}
function(const function& other) : callable(other.callable ? other.callable->clone() : nullptr) {}
function(function&& other) noexcept : callable(std::move(other.callable)) {
other.callable = nullptr;
}
function& operator=(const function& other) {
if (this != &other) {
callable.reset(other.callable ? other.callable->clone() : nullptr);
}
return *this;
}
function& operator=(function&& other) noexcept {
if (this != &other) {
callable = std::move(other.callable);
other.callable = nullptr;
}
return *this;
}
R operator()(Args... args) const {
if (!callable) {
throw std::bad_function_call();
}
return callable->invoke(std::forward<Args>(args)...);
}
explicit operator bool() const noexcept {
return callable != nullptr;
}
private:
struct CallableBase {
virtual ~CallableBase() = default;
virtual R invoke(Args... args) const = 0;
virtual CallableBase* clone() const = 0;
};
template <typename F>
struct CallableType : CallableBase {
F f;
explicit CallableType(F&& func) : f(std::forward<F>(func)) {}
R invoke(Args... args) const override {
return f(std::forward<Args>(args)...);
}
CallableBase* clone() const override {
return new CallableType(f);
}
};
std::unique_ptr<CallableBase> callable;
};

1. 模板参数详解

std::function 的模板参数是一个函数类型,例如 std::function<int(int, int)>。其中 int(int, int) 是函数类型,int 是返回类型,(int, int) 是参数列表。这种形式允许 std::function 存储任何与该签名匹配的可调用对象。

2. 内部的多态与深拷贝

std::function 的实现中,使用了一个名为 CallableBase 的抽象基类,用于定义一个通用的接口,存储和调用被封装的函数对象。CallableBase 是一个纯虚类,包含 invokeclone 两个虚函数。不同的函数对象(如普通函数、Lambda 等)会派生出具体的类,实现这些虚函数以实现多态。

struct CallableBase { virtual ~CallableBase() {} virtual R invoke(Args... args) = 0; virtual CallableBase* clone() const = 0;};

4. 主要使用场景及示例

1. 回调函数

回调函数是在异步操作完成后调用的函数,通常用于通知操作的结果或状态。std::function 允许将任意类型的回调函数传递给异步操作:

void asyncOperation(const std::function<void(int)>& callback) { // 模拟异步操作 callback(42);}

2. 多态性

std::function 可以存储和调用不同类型的可调用对象,这使得在需要多态行为的场景下非常有用:

void callFunction(const std::function<void()>& func) { func(); // 调用传递进来的任意可调用对象}

3. 函数对象的存储与传递

你可以使用 std::function 来存储和传递多个函数对象,然后在需要时调用它们:

std::vector<std::function<void(int)>> funcs;funcs.push_back([](int x) { std::cout << "Lambda called with: " << x << std::endl; });

5. 高级应用:事件处理系统与不定长参数

在某些情况下,事件处理系统需要处理不定长参数。通过结合 std::function 和模板参数包 Args...,可以实现更加灵活的事件处理机制:

#include <iostream>#include <functional>#include <map>#include <string>class EventSystem {public: // 使用模板和参数包注册事件处理函数 template<typename... Args> void registerEvent(const std::string& eventName, std::function<void(Args...)> handler) { handlers[eventName] = [handler](std::vector<std::any> args) { // 将 std::any 转换回原始类型并调用处理函数 invokeHandler<Args...>(handler, args, std::index_sequence_for<Args...>{}); }; } // 使用模板和参数包触发事件 template<typename... Args> void triggerEvent(const std::string& eventName, Args... args) { auto it = handlers.find(eventName); if (it != handlers.end()) { // 将参数打包为 std::vector<std::any> 传递给通用调用器 it->second({std::any(args)...}); } }private: // 存储通用事件处理函数(使用 std::any 作为参数类型) std::map<std::string, std::function<void(std::vector<std::any>)>> handlers; // 帮助函数:将 std::any 转换为原始类型并调用处理函数 template<typename... Args, std::size_t... I> static void invokeHandler(std::function<void(Args...)> handler, const std::vector<std::any>& args, std::index_sequence<I...>) { handler(std::any_cast<Args>(args[I])...); }};

示例

完整示例展示了如何使用 std::function 实现带有不定长参数的事件处理系统:

int main() { EventSystem es; // 注册带参数的事件处理函数 es.registerEvent<int, int>("onAdd", [](int a, int b) { std::cout << "Sum: " << a + b << std::endl; }); // 触发事件并传递参数 es.triggerEvent("onAdd", 3, 5); return 0;}

6. 总结与反思

std::function 是 C++11 中一个非常重要的新特性,它大大增强了 C++ 处理函数对象的灵活性。无论是简单的回调函数,还是复杂的事件系统,std::function 都提供了强大的支持。通过对其内部实现的理解,可以更好地利用它来编写高效、灵活的代码。希望本教程对你深入理解 std::function 提供了帮助。