C++11环境下替代boost::optional<std::shared_ptr<Data>>的参数传递方案
兼容C++11的可选非所有权参数传递方案
问题背景
现有代码里大量用boost::optional<std::shared_ptr<Data>>当函数参数,但函数根本不需要拿Data的所有权——拷贝shared_ptr既费性能,还容易让调用的人误以为函数会持有这个对象。要是不需要可选特性,我们肯定直接用Data const&,但C11没原生的可选引用,之前想过传Data const*裸指针,虽然语义对,但总被团队质疑安全性。现在要找个兼容C11、比裸指针更靠谱的替代方案。
方案1:自定义可选const引用包装器
把Boost的reference_wrapper和boost::optional结合,搞出一个可选的const引用类型,完全避开shared_ptr拷贝,语义也直白。
代码示例:
#include <boost/optional.hpp> #include <functional> // C++11原生支持std::reference_wrapper // 定义可选const引用的别名,方便复用 using OptionalDataRef = boost::optional<std::reference_wrapper<const Data>>; // 函数参数用这个类型 void process_data(OptionalDataRef opt_data) { if (opt_data) { const Data& data = opt_data->get(); // 直接操作data,不用拷贝shared_ptr } } // 调用方式 // 有数据的情况: std::shared_ptr<Data> ptr = ...; process_data(std::cref(*ptr)); // 无数据的情况: process_data(boost::none);
优点:
- 完全没
shared_ptr拷贝,性能拉满 - 语义明确:
reference_wrapper说明是引用(不拿所有权),boost::optional说明是可选参数 - 调用时用
std::cref显式转换,避免隐式转换带来的误解
缺点: - 需要自定义类型别名,团队得先熟悉
reference_wrapper的用法
方案2:纯Boost组件的可选引用
和方案1逻辑一样,换成Boost自带的reference_wrapper,和现有代码里boost::optional的风格更统一:
#include <boost/optional.hpp> #include <boost/ref.hpp> using OptionalDataRef = boost::optional<boost::reference_wrapper<const Data>>; void process_data(OptionalDataRef opt_data) { if (opt_data) { const Data& data = opt_data->get(); // 处理逻辑 } } // 调用 std::shared_ptr<Data> ptr = ...; process_data(boost::cref(*ptr)); process_data(boost::none);
优点:
- 全用Boost组件,和现有代码的Boost用法对齐
- 同样无拷贝,语义清晰
缺点: - 依赖Boost的
ref组件,但项目本来就在用Boost,基本没影响
方案3:重载函数(分有无参数版本)
如果可选场景就两种:有数据、没数据,直接重载函数就行,完全不用可选类型:
// 有数据时调用的版本 void process_data(const Data& data) { // 正常处理数据的逻辑 } // 无数据时调用的版本 void process_data() { // 没数据时的处理逻辑 } // 调用方式 std::shared_ptr<Data> ptr = ...; if (ptr) { process_data(*ptr); } else { process_data(); }
优点:
- 最直观,谁看都懂
- 没额外类型包装,性能最优
缺点: - 要是无数据的逻辑和有数据的逻辑高度重复,会导致代码冗余
- 可选参数多了的话,重载数量会爆炸,不适合多可选参数的场景
方案4:带可选包装的裸指针
如果团队质疑裸指针是怕别人误解成要管理所有权,那用boost::optional<const Data*>就能明确这是可选的非所有权指针:
#include <boost/optional.hpp> void process_data(boost::optional<const Data*> opt_data) { if (opt_data && *opt_data) { // 先判断optional有值,再判断指针非空 const Data& data = **opt_data; // 处理逻辑 } } // 调用 std::shared_ptr<Data> ptr = ...; process_data(ptr ? &*ptr : nullptr); process_data(boost::none); // 或者直接传nullptr,boost::optional能隐式转换
优点:
- 语义比单纯的
const Data*清楚:boost::optional明确是可选,指针明确不拿所有权 - 实现简单,不用额外搞类型包装
缺点: - 调用时要手动判断
shared_ptr是否为空再取地址,有点麻烦 - 还是用到了裸指针,可能还是会有人质疑,但比单纯传裸指针语义明确多了
方案选择建议
- 追求极致性能和清晰语义,优先选方案1或方案2,两者本质一样,看团队更习惯用标准库还是Boost的
reference_wrapper - 可选场景简单(只有有无两种情况),且无数据逻辑独立,优先选方案3,代码最直白
- 团队能接受带可选包装的裸指针,方案4是最简单的过渡方案
内容的提问来源于stack exchange,提问作者jcoder
相关产品推荐
相关产品推荐

