持有unique_ptr向量所有权后,工具函数的参数选型疑问
关于unique_ptr容器工具函数的设计疑问
问题背景
现有代码如下:
vector<unique_ptr<Foo>> foos = ... // 初始化向量 // 执行一些操作 func(std::move(foos)); // func现在拥有foos的所有权,我不再使用它们
// 获取foos的所有权 void func(vector<unique_ptr<Foo>>&& foos);
现在需要在func中重构出一个使用foos的工具函数,目前有两种方案:
void util_func1(const vector<Foo*>& foos); // 不获取foos的所有权 void util_func2(const vector<unique_ptr<Foo>>& foos); // 不获取foos的所有权
第一种方案符合“获取所有权传值,不获取则传裸指针/引用”的unique_ptr使用建议,但func中需要做繁琐的转换:
void func(vector<unique_ptr<Foo>>&& foos) { vector<Foo*> raw_ptr_foos; raw_ptr_foos.reserve(foos.size()); for (auto foo : foos) raw_ptr_foos.push_back(foo.get()); util_func1(raw_ptr_foos); }
而第二种方案是传递unique_ptr容器的const引用,听说不建议这么做。疑问如下:
util_func2是否为正确方案?- 还是应该接受繁琐的裸指针向量转换?
- 是否有第三种更优的方案?
解答
1. util_func2是完全正确的方案
所谓“不建议传递unique_ptr的const引用”,通常针对的是单个unique_ptr的场景:单个const unique_ptr无法转移所有权,传引用容易让调用者误解函数可能修改所有权,但对于const vector<unique_ptr<Foo>>&这种容器的const引用,语义非常明确:
- 工具函数不会修改容器本身(const保证)
- 不会获取容器内元素的所有权(只是读取unique_ptr指向的对象,没有调用
release()或move())
这种用法合法、安全,且完全符合代码语义,不需要担心所谓的“不建议”。
2. 没必要接受裸指针向量的转换
除非util_func1需要和不使用智能指针的旧代码兼容,否则这种转换完全是冗余的——既增加了代码量,又额外创建了一个裸指针容器,没有任何收益。
3. 第三种更灵活的方案
可以根据工具函数的逻辑,选择以下两种更优的设计:
方案A:使用迭代器或范围(C++20+)
让工具函数接受迭代器范围或通用范围,适配任意容器类型,同时避免容器转换:
// 迭代器版本 template <typename Iterator> void util_func(Iterator begin, Iterator end) { for (auto it = begin; it != end; ++it) { // 使用 (*it)->xxx 访问Foo对象 (*it)->do_something(); } } // 在func中调用 util_func(foos.begin(), foos.end());
或者用C++20的范围特性,代码更简洁:
#include <ranges> void util_func(std::ranges::input_range auto&& range) { for (const auto& foo_ptr : range) { foo_ptr->do_something(); } } // 在func中直接调用 util_func(foos);
方案B:拆分单个元素的工具函数
如果工具函数的逻辑是针对单个Foo对象的,直接设计成处理单个元素的函数,再在func中遍历容器调用:
// 针对单个Foo对象的工具函数 void util_func(const Foo& foo) { foo.do_something(); } // 在func中调用 for (const auto& foo_ptr : foos) { util_func(*foo_ptr); }
这种设计更灵活,工具函数的复用性更强,也避免了传递整个容器的开销。
内容的提问来源于stack exchange,提问作者Benjy Kessler
相关产品推荐
相关产品推荐

