关于 std::reference_wrapper
和 std::function
的详细介绍及具体测试用例:
1. std::reference_wrapper
(引用包装器)
核心功能
- 包装引用:将引用转换为可拷贝、可赋值的对象
- 支持隐式转换:可自动转换为原始引用类型
- 容器友好:允许在 STL 容器中存储引用(原生 C++ 数组引用无法直接存入容器)
典型应用场景
- 在容器中存储大型对象的引用(避免拷贝)
- 需要传递引用的模板元编程场景
- 配合算法使用引用语义(如
std::for_each
修改原集合)
测试用例
#include <functional>
#include <vector>
#include <algorithm>
#include <iostream>
void modify(int& x) {
x *= 2;
}
int main() {
// 场景1:容器存储引用
int a = 10, b = 20, c = 30;
std::vector<std::reference_wrapper<int>> nums = {a, b, c};
// 通过引用修改原始值
for (auto& ref : nums) ref.get() += 5;
std::cout << "修改后: " << a << ", " << b << ", " << c << "\n";
// 输出: 修改后: 15, 25, 35
// 场景2:配合算法使用
std::for_each(nums.begin(), nums.end(), modify);
std::cout << "算法处理: " << a << ", " << b << ", " << c << "\n";
// 输出: 算法处理: 30, 50, 70
// 场景3:隐式转换
int& original_ref = nums[0]; // 自动转换为 int&
original_ref = 100;
std::cout << "隐式转换后: " << a << "\n"; // 输出: 100
return 0;
}
2. std::function
(通用函数包装器)
核心功能
- 类型擦除:统一存储各种可调用对象(函数指针、lambda、成员函数等)
- 延迟执行:将函数作为参数传递或返回值
- 运行时多态:动态绑定不同函数实现
典型应用场景
- 回调函数机制(如事件处理)
- 命令模式实现
- 动态策略模式
测试用例
#include <functional>
#include <iostream>
#include <string>
// 普通函数
void greet(const std::string& name) {
std::cout << "Hello, " << name << "!\n";
}
// 函数对象
struct Farewell {
void operator()(const std::string& name) const {
std::cout << "Goodbye, " << name << "!\n";
}
};
class Person {
public:
void setName(const std::string& name) {
m_name = name;
}
void showName() const {
std::cout << "My name is " << m_name << "\n";
}
private:
std::string m_name;
};
int main() {
// 包装普通函数
std::function<void(const std::string&)> func1 = greet;
func1("Alice"); // 输出: Hello, Alice!
// 包装函数对象
Farewell farewell;
std::function<void(const std::string&)> func2 = farewell;
func2("Bob"); // 输出: Goodbye, Bob!
// 包装 lambda
auto lambda = [](const std::string& s) {
std::cout << "Lambda: " << s << "\n";
};
std::function<void(const std::string&)> func3 = lambda;
func3("Charlie"); // 输出: Lambda: Charlie
// 包装成员函数
Person person;
person.setName("David");
// 绑定成员函数(需要对象实例)
std::function<void()> func4 = std::bind(&Person::showName, &person);
func4(); // 输出: My name is David
// 包装带参数的成员函数
std::function<void(const std::string&)> func5 =
std::bind(&Person::setName, &person, std::placeholders::_1);
func5("Eve");
person.showName(); // 输出: My name is Eve
// 空函数检查
std::function<void()> empty_func;
if (!empty_func) {
std::cout << "空函数对象\n"; // 会执行
}
return 0;
}
3. 关键对比总结
特性 | std::reference_wrapper | std::function |
---|---|---|
主要用途 | 包装引用,实现引用语义的容器存储 | 统一管理各种可调用对象 |
类型安全性 | 强类型(模板参数指定类型) | 通过函数签名约束(如 void(int) ) |
性能影响 | 零开销(编译时解析) | 有运行时开销(类型擦除) |
典型操作 | get() , 隐式转换 | operator() , bool 转换检查 |
4. 使用注意事项
(1) reference_wrapper
-
不能包装临时对象(悬挂引用风险)
-
不能替代智能指针(不管理生命周期)
-
与
auto
配合时需注意类型推导:auto ref = std::ref(a); // 类型是 reference_wrapper<int> int& r = ref; // 需要显式转换或使用 get()
(2) std::function
-
空状态检查:调用空
function
会抛出std::bad_function_call
-
性能关键场景慎用(相比虚函数有额外开销)
-
存储成员函数时需绑定对象实例:
// 正确做法 std::function<void()> f = std::bind(&Class::method, &obj);
5. 进阶用法示例
事件系统(std::function
应用)
#include <functional>
#include <vector>
class Button {
public:
using Callback = std::function<void()>;
void addClickListener(Callback cb) {
listeners.push_back(cb);
}
void click() {
for (auto& cb : listeners) {
if (cb) cb();
}
}
private:
std::vector<Callback> listeners;
};
// 使用示例
void playSound() { /* ... */ }
int main() {
Button btn;
btn.addClickListener([] { std::cout << "点击事件1\n"; });
btn.addClickListener(playSound);
btn.click(); // 触发所有回调
return 0;
}
总结
reference_wrapper
:
当需要容器存储引用或模板需要推导引用类型时使用,避免不必要的拷贝。std::function
:
当需要统一处理不同类型的回调函数或实现运行时多态时使用,为现代 C++ 回调系统的核心组件。
两者结合使用可以构建灵活高效的抽象机制,例如在事件系统中用 reference_wrapper
传递对象引用,用 std::function
管理事件处理方法。