You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:28:09