使用std::map存储vector<unique_ptr<纯虚类>>的编译错误及优化需求
在std::map中使用vector<unique_ptr >作为值的正确方式
我需要在std::map中存储vector<vector<unique_ptr<Base>>>类型的值,但使用insert方法时遇到了编译错误,已知std::unique_ptr不可拷贝,求正确的实现方式。
示例代码
#include <iostream> #include <algorithm> #include <vector> #include <map> class Base { public: virtual ~Base() = default; virtual void doIt() = 0; public: int base = 1; }; class Derived : public Base{ public: Derived(int x) : derived(x) { base = x; } void doIt() override { printf("hello from derived class. %d, %d\n", derived, base); } public: int derived = 2; }; class Storage { public: std::map<std::string, std::vector<std::vector<std::unique_ptr<Base>>>> cache; void add_to_cache(std::unique_ptr<Base> ptr) { cache["row"].back().push_back(std::move(ptr)); } void add_new_cacheLine() { cache.emplace("row", std::vector<std::vector<std::unique_ptr<Base>>>{}); // Case I: 编译通过 /* std::vector<std::vector<std::unique_ptr<Base>>> emptyCacheLine{}; cache.insert("row", emptyCacheLine); // Case II: 编译错误 */ } Storage() { cache["row"] = {}; } }; int main() { Storage storage; storage.add_to_cache(std::make_unique<Derived>(3)); for (auto& s : storage.cache["row"].back()) { s->doIt(); } }
编译错误信息
使用MSVC编译Case II时,出现如下错误:
Error C2280 'std::unique_ptr<Base,std::default_delete<Base>> &std::unique_ptr<Base,std::default_delete<Base>>::operator =(const std::unique_ptr<Base,std::default_delete<Base>> &)': attempting to reference a deleted function
完整构建日志
Build started... 1>------ Build started: Project: ConsoleApplication1, Configuration: Debug x64 ------ 1>ConsoleApplication1.cpp 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(677,47): error C2280: 'std::unique_ptr<Base,std::default_delete<Base>>::unique_ptr(const std::unique_ptr<Base,std::default_delete<Base>> &)': attempting to reference a deleted function 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\memory(3317,5): message : see declaration of 'std::unique_ptr<Base,std::default_delete<Base>>::unique_ptr' 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\memory(3317,5): message : 'std::unique_ptr<Base,std::default_delete<Base>>::unique_ptr(const std::unique_ptr<Base,std::default_delete<Base>> &)': function was explicitly deleted 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(1777,35): message : see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,std::unique_ptr<Base,std::default_delete<Base>>&>(_Alloc &,_Objty *const ,std::unique_ptr<Base,std::default_delete<Base>> &)' being compiled 1> with 1> [ 1> _Alloc=std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>, 1> _Ty=std::unique_ptr<Base,std::default_delete<Base>>, 1> _Objty=std::unique_ptr<Base,std::default_delete<Base>> 1> ] 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(1831,17): message : see reference to function template instantiation 'void std::_Uninitialized_backout_al<std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>::_Emplace_back<std::unique_ptr<Base,std::default_delete<Base>>&>(std::unique_ptr<Base,std::default_delete<Base>> &)' being compiled 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\vector(2072,36): message : see reference to function template instantiation 'std::unique_ptr<Base,std::default_delete<Base>> *std::_Uninitialized_copy<std::unique_ptr<Base,std::default_delete<Base>>*,std::unique_ptr<Base,std::default_delete<Base>>*,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>(_InIt,_Se,std::unique_ptr<Base,std::default_delete<Base>> *,_Alloc &)' being compiled 1> with 1> [ 1> _InIt=std::unique_ptr<Base,std::default_delete<Base>> *, 1> _Se=std::unique_ptr<Base,std::default_delete<Base>> *, 1> _Alloc=std::allocator<std::unique_ptr<Base,std::default_delete<Base>>> 1> ] 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\vector(684,9): message : see reference to function template instantiation 'void std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>::_Construct_n<std::unique_ptr<Base,std::default_delete<Base>>*const &,std::unique_ptr<Base,std::default_delete<Base>>*const &>(const unsigned __int64,std::unique_ptr<Base,std::default_delete<Base>> *const &,std::unique_ptr<Base,std::default_delete<Base>> *const &)' being compiled 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\vector(680,5): message : while compiling class template member function 'std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>::vector(const std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>> &)' 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(677,47): message : see the first reference to 'std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>::vector' in 'std::_Default_allocator_traits<_Alloc>::construct' 1> with 1> _Alloc=std::allocator<std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>> 1> ] 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(1777,44): message : see the first reference to 'std::_Default_allocator_traits<_Alloc>::construct' in 'std::_Uninitialized_backout_al<std::allocator<std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>>>::_Emplace_back' 1> with 1> [ 1> _Alloc=std::allocator<std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>> 1> ] 1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.36.32532\include\xmemory(1831,31): message : see the first reference to 'std::_Uninitialized_backout_al<std::allocator<std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>>>::_Emplace_back' in 'std::_Uninitialized_copy' 1>C:\Users\xxx\source\repos\ConsoleApplication1\ConsoleApplication1.cpp(30,1): message : see reference to class template instantiation 'std::vector<std::unique_ptr<Base,std::default_delete<Base>>,std::allocator<std::unique_ptr<Base,std::default_delete<Base>>>>' being compiled 1>Done building project "ConsoleApplication1.vcxproj" -- FAILED. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== ========== Build started at 2:48 PM and took 00.963 seconds ==========
问题分析与解决
问题原因
std::map::insert要求传递可拷贝的对象,但std::unique_ptr是独占所有权的智能指针,其拷贝构造函数和拷贝赋值运算符被显式删除;而vector<unique_ptr<Base>>会因为元素不可拷贝,导致整个vector的拷贝构造函数也被禁用。Case II中cache.insert("row", emptyCacheLine)尝试拷贝emptyCacheLine,直接触发编译错误。
正确实现方式
方式1:使用emplace直接构造(推荐)
std::map::emplace可以直接在map内部构造值对象,完全避免拷贝操作:
void add_new_cacheLine() { // 直接构造包含一个空内层vector的外层vector,避免back()访问空容器 cache.emplace("row", std::vector<std::vector<std::unique_ptr<Base>>>{std::vector<std::unique_ptr<Base>>{}}); }
方式2:使用std::move转移所有权
如果已经创建了emptyCacheLine对象,可通过std::move将其所有权转移给map,利用vector的移动构造函数完成操作:
void add_new_cacheLine() { std::vector<std::vector<std::unique_ptr<Base>>> emptyCacheLine{}; emptyCacheLine.push_back({}); // 初始化内层空vector cache.insert(std::make_pair("row", std::move(emptyCacheLine))); }
方式3:结合operator[]与std::move赋值
如果key不存在,operator[]会默认构造值对象,也可以直接移动赋值:
void add_new_cacheLine() { std::vector<std::vector<std::unique_ptr<Base>>> emptyCacheLine{}; emptyCacheLine.push_back({}); cache["row"] = std::move(emptyCacheLine); }
补充优化:初始化时避免空容器
为了防止cache["row"].back()因外层vector为空触发运行时错误,建议在Storage构造函数中直接初始化内层vector:
Storage() { cache["row"].push_back({}); // 确保外层vector至少有一个内层元素 }
内容的提问来源于stack exchange,提问作者undersilence
相关产品推荐
相关产品推荐

