如何创建具备类似引用语义的C++类型包装器?
如何实现将值语义类型包装为表现如引用的对象?
需求描述
需要一个包装类Wrap<T>,让值语义类型T表现出引用语义:多个容器中的Wrap<T>对象共享同一个底层值,修改其中一个会同步影响所有其他对象,示例代码如下:
int a = 20; std::list<Wrap<int>> l1; l1.push_back(a); std::list<Wrap<int>> l2; l2.push_back(a); l2.front() = 10; cout << l1.front() << endl; // 期望输出10
同时有以下疑问:
- 考虑过
std::shared_ptr,但不确定指针语义是否符合需求 - 想知道标准库中有无现成替代方案
- 发现
std::reference_wrapper是将引用当作值类型使用,与需求相反,该如何解决?
解决方案
核心思路
你的需求本质是让多个包装对象共享同一个底层值实例,并自动管理值的生命周期。std::shared_ptr其实是最贴合需求的标准库工具,它提供了共享所有权的指针语义,配合简单的运算符重载就能让它表现得像值类型。
1. 自定义包装类(推荐)
基于std::shared_ptr封装一个Wrap类,重载必要的运算符,让使用方式完全贴近值类型:
#include <iostream> #include <list> #include <memory> template<typename T> class Wrap { private: std::shared_ptr<T> m_ptr; public: // 构造:接受值类型,创建共享实例 explicit Wrap(const T& val) : m_ptr(std::make_shared<T>(val)) {} Wrap(T&& val) : m_ptr(std::make_shared<T>(std::move(val))) {} // 拷贝/赋值默认即可,shared_ptr自动处理引用计数 Wrap(const Wrap&) = default; Wrap& operator=(const Wrap&) = default; // 重载赋值运算符:修改底层值 T& operator=(const T& val) { *m_ptr = val; return *m_ptr; } T& operator=(T&& val) { *m_ptr = std::move(val); return *m_ptr; } // 隐式转换为T&,支持直接读取和修改 operator T&() { return *m_ptr; } operator const T&() const { return *m_ptr; } }; int main() { int a = 20; std::list<Wrap<int>> l1; l1.push_back(a); std::list<Wrap<int>> l2; l2.push_back(a); l2.front() = 10; std::cout << l1.front() << std::endl; // 输出10 return 0; }
这个包装类的优势:
- 完全满足你给出的示例效果,使用方式和普通值类型一致
- 自动管理底层值的生命周期,即使原始的
a被销毁,容器中的Wrap对象仍能安全访问共享值 - 支持移动语义,性能高效
2. 直接使用std::shared_ptr
如果不想自定义类,直接用std::shared_ptr也能实现需求,只是使用时需要显式解引用:
#include <iostream> #include <list> #include <memory> int main() { int a = 20; std::list<std::shared_ptr<int>> l1; auto shared_val = std::make_shared<int>(a); l1.push_back(shared_val); std::list<std::shared_ptr<int>> l2; l2.push_back(shared_val); // 共享同一个实例 *l2.front() = 10; std::cout << *l1.front() << std::endl; // 输出10 return 0; }
注意:必须让多个容器的shared_ptr指向同一个实例,不能各自调用std::make_shared<int>(a)(否则会创建独立的实例,无法共享修改)。
关于std::reference_wrapper
你说的没错,std::reference_wrapper确实和需求相反:它的作用是将已有对象的引用包装成可拷贝的对象,让容器能存储引用类型(容器不能直接存裸引用)。但它依赖于原始对象的生命周期,一旦原始对象(比如示例中的a)被销毁,容器中的reference_wrapper就会变成悬空引用,存在安全隐患。而且它无法创建新的共享实例,只能绑定已有对象,不符合你需求中“包装值语义类型”的核心诉求。
标准库的替代方案
标准库中没有专门为“值语义转引用语义”设计的包装器,因为std::shared_ptr已经提供了核心的共享所有权机制,自定义一层包装就能满足语法上的需求。
内容的提问来源于stack exchange,提问作者user2138149
相关产品推荐
相关产品推荐

