能否实现仅存储左值引用或对象的C++模板类?
实现兼具左值引用存储与右值对象存储的C++模板类
你要的这个类是一个"可区分引用/值"的包装器,核心要解决两个痛点:消除std::optional的无意义构造开销,优化get()的冗余分支,同时让函数能统一接收左值、右值构造的包装器。以下是两种可行的实现方案:
方案一:用Union手动管理存储(极致高效)
这种方式直接用union存储指针或对象,完全避免不必要的构造开销,手动控制析构逻辑:
#include <utility> // std::move template<typename T> class ref { private: union Storage { T* ptr; T obj; Storage() {} // 不初始化任何成员,避免默认构造开销 ~Storage() {} // 不自动析构,由外部控制 } m_storage; bool m_is_lvalue; // 辅助函数:按需析构对象 void destroy_if_object() noexcept { if (!m_is_lvalue) { m_storage.obj.~T(); } } public: // 左值构造:存储指针 ref(T& lvalue) noexcept : m_is_lvalue(true) { new (&m_storage.ptr) T*(&lvalue); } // 右值构造:存储移动构造的对象 ref(T&& rvalue) noexcept(std::is_nothrow_move_constructible_v<T>) : m_is_lvalue(false) { new (&m_storage.obj) T(std::move(rvalue)); } // 析构函数:按需清理资源 ~ref() noexcept { destroy_if_object(); } // 禁用拷贝(避免指针悬空或对象拷贝) ref(const ref&) = delete; ref& operator=(const ref&) = delete; // 移动构造:转移资源,无拷贝 ref(ref&& other) noexcept(std::is_nothrow_move_constructible_v<T>) : m_is_lvalue(other.m_is_lvalue) { if (m_is_lvalue) { new (&m_storage.ptr) T*(other.m_storage.ptr); } else { new (&m_storage.obj) T(std::move(other.m_storage.obj)); // 标记原对象为左值,避免其析构时销毁已转移的对象 other.m_is_lvalue = true; } } // 移动赋值 ref& operator=(ref&& other) noexcept(std::is_nothrow_move_constructible_v<T>) { if (this == &other) return *this; // 先清理自身资源 destroy_if_object(); m_is_lvalue = other.m_is_lvalue; if (m_is_lvalue) { m_storage.ptr = other.m_storage.ptr; } else { new (&m_storage.obj) T(std::move(other.m_storage.obj)); other.m_is_lvalue = true; } return *this; } // 获取引用 T& get() noexcept { return m_is_lvalue ? *m_storage.ptr : m_storage.obj; } const T& get() const noexcept { return m_is_lvalue ? *m_storage.ptr : m_storage.obj; } // 重载运算符,简化使用 T* operator->() noexcept { return &get(); } const T* operator->() const noexcept { return &get(); } T& operator*() noexcept { return get(); } const T& operator*() const noexcept { return get(); } }; // 推导指南,允许自动推导模板参数 template<typename T> ref(T&) -> ref<T>; template<typename T> ref(T&&) -> ref<T>;
方案优势:
- 零冗余存储:union仅占用
max(sizeof(T*), sizeof(T))的空间,无额外开销 - 无不必要构造:存储左值时完全不构造T对象,彻底解决原实现中
std::optional的默认构造问题 - 移动语义高效:仅转移资源,无拷贝操作
方案二:用std::variant实现(安全简洁)
如果不想手动处理union的构造析构,可以用C++17的std::variant,它会自动管理成员的生命周期,代码更简洁安全:
#include <variant> // std::variant #include <functional> // std::reference_wrapper #include <utility> // std::move template<typename T> class ref { private: std::variant<std::reference_wrapper<T>, T> m_data; public: // 左值构造:存储引用包装器 ref(T& lvalue) noexcept : m_data(std::ref(lvalue)) {} // 右值构造:存储移动构造的对象 ref(T&& rvalue) noexcept(std::is_nothrow_move_constructible_v<T>) : m_data(std::move(rvalue)) {} // 禁用拷贝,保证无拷贝操作 ref(const ref&) = delete; ref& operator=(const ref&) = delete; // 移动构造和赋值默认即可,std::variant会自动处理 ref(ref&&) noexcept = default; ref& operator=(ref&&) noexcept = default; // 获取引用:用std::visit配合编译时分支消除冗余检查 T& get() noexcept { return std::visit([](auto& val) -> T& { if constexpr (std::is_same_v<decltype(val), std::reference_wrapper<T>&>) { return val.get(); } else { return val; } }, m_data); } const T& get() const noexcept { return std::visit([](auto& val) -> const T& { if constexpr (std::is_same_v<decltype(val), const std::reference_wrapper<T>&>) { return val.get(); } else { return val; } }, m_data); } // 重载运算符 T* operator->() noexcept { return &get(); } const T* operator->() const noexcept { return &get(); } T& operator*() noexcept { return get(); } const T& operator*() const noexcept { return get(); } }; // 推导指南 template<typename T> ref(T&) -> ref<T>; template<typename T> ref(T&&) -> ref<T>;
方案优势:
- 代码简洁安全:无需手动管理构造析构,
std::variant自动处理生命周期 - 编译时分支优化:
if constexpr让编译器为两种情况生成专属代码,运行时开销极低
使用方式
无论是哪种方案,你都可以像这样使用:
struct bar { /* ... */ }; void foo(ref<bar> r) { // 三种访问方式等价 r.get().some_method(); (*r).some_method(); r->some_method(); } int main() { bar lvalue_bar; foo(lvalue_bar); // 自动构造ref<bar>存储左值引用 foo(bar{}); // 自动构造ref<bar>存储右值对象 }
关于运行时检查的说明
因为你需要ref<T>作为统一的类型,同时兼容左值和右值构造的情况,运行时的类型判断是不可避免的。但上述两种方案都将开销降到了最低:要么是一个bool判断,要么是std::variant的索引判断,编译器通常会将其优化为极高效的分支。
内容的提问来源于stack exchange,提问作者TwistedBlizzard
相关产品推荐
相关产品推荐

