如何在C++模板中透明处理std::vector<T>与std::vector<std::shared_ptr<T>>
你需要让同一个模板算法兼容两种std::vector:一种存储类型T的对象,另一种存储指向T的std::shared_ptr<T>,在模板内部自动区分元素类型,对shared_ptr自动解引用,普通对象则直接使用,且不想让客户端代码复杂化。
方法:利用函数重载实现自动解引用
最简洁且兼容旧标准的方式是通过重载辅助函数,让编译器自动根据元素类型选择对应的处理逻辑,完全不需要客户端额外传参。
完整代码示例:
#include <vector> #include <memory> struct S { void member() const {} }; // 处理普通对象的重载:直接返回原对象引用 template <typename T> const T& deref(const T& val) { return val; } // 处理std::shared_ptr的重载:返回指针指向的对象引用 template <typename T> const T& deref(const std::shared_ptr<T>& ptr) { return *ptr; } void fn(const auto& arr) { for (const auto& val : arr) { // 自动匹配对应deref重载,无需手动判断类型 const auto& obj = deref(val); obj.member(); } } int main() { std::vector<S> objects; std::vector<std::shared_ptr<S>> pointers; fn(objects); fn(pointers); }
原理说明
- 辅助函数
deref提供两个重载:一个接收普通对象引用,直接返回;另一个接收std::shared_ptr,返回解引用后的对象引用。 - 编译器在编译时会根据
val的实际类型,自动匹配对应的重载版本,实现透明的解引用逻辑。 - 客户端调用
fn时无需做任何额外处理,完全感受不到内部的类型区分逻辑。
C++17及以上可选方案:if constexpr编译期判断
如果使用C++17及以上标准,也可以直接通过编译期类型判断实现,无需额外重载函数:
#include <vector> #include <memory> #include <type_traits> struct S { void member() const {} }; // 类型 trait:判断是否为std::shared_ptr template <typename T> struct is_shared_ptr : std::false_type {}; template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {}; template <typename T> constexpr bool is_shared_ptr_v = is_shared_ptr<T>::value; void fn(const auto& arr) { for (const auto& val : arr) { const auto& obj = [&]() -> const S& { // 编译时判断类型,选择对应的分支 if constexpr (is_shared_ptr_v<std::decay_t<decltype(val)>>) { return *val; } else { return val; } }(); obj.member(); } } int main() { std::vector<S> objects; std::vector<std::shared_ptr<S>> pointers; fn(objects); fn(pointers); }
这种方式在lambda内部通过if constexpr实现编译期分支选择,同样不会影响客户端代码的简洁性。
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

