在 std::map 中处理 unique_ptr<pure_virtual_class> 向量时出现意外编译错误

Unexpected compile error when dealing with vector of unique_ptr<pure_virtual_class> inside std::map

提问人:undersilence 提问时间:7/25/2023 最后编辑:undersilence 更新时间:9/7/2023 访问量:150

问:

#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: compile ok.
/*
  std::vector<std::vector<std::unique_ptr<Base>>> emptyCacheLine{};
  cache.insert("row", emptyCacheLine); // Case II: compile error
*/
  }

  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 和案例 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>        [
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 ==========

我知道unique_ptr无法复制,但是有没有办法将unique_ptr的向量用作 std::map 中的值?

  • 编辑:在注释的帮助下,现在我可以通过编译,但我真正需要的是用里面的一个std::vector初始化emptyCacheLine,例如,因为目前它在调用时会出现运行时错误。多谢!emptyCacheLine.push_back({})cache["tag"].back()add_to_cache
c++ stdvector unique-ptr stdmap pure-virtual

评论

1赞 Some programmer dude 7/25/2023
这是您收到的唯一错误消息吗?您是否查看了完整和完整的构建日志?您在哪一行收到错误?请编辑您的问题,在代码中为该行添加注释。
2赞 273K 7/25/2023
cache["row"] = {};- 这会在地图中创建一个空向量,在右侧创建一个空向量,将第二个向量复制到第一个向量中,这是无法做到的,因为向量元素是不可复制的。
1赞 Some programmer dude 7/25/2023
关于地图,如果您使用尚不存在的元素,则会自动创建该元素。所以你实际上并不需要.无论如何,它都会起作用。cache["row"] = {}
1赞 Daniel Langr 7/25/2023
如果需要提前创建该元素,只需编写 .cache["row"];
3赞 Daniel Langr 7/25/2023
@273K 真的是在右侧创建的矢量吗?似乎首选接受的向量赋值运算符的重载。测试:godbolt.org/z/Tbn1vadzMstd::initializer_list

答:

1赞 Polarcode 7/25/2023 #1

对于缓存类型,您可以在构造函数中使用一个空向量初始化最内层的向量,如下所示:std::map<std::string, std::vector<std::vector<std::unique_ptr<Base>>>>

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) {
    if (cache["row"].empty())
      cache["row"].emplace_back(); // Add an empty vector if none exists
    cache["row"].back().push_back(std::move(ptr));
  }

  void add_new_cacheLine() {
    cache.emplace("row", std::vector<std::vector<std::unique_ptr<Base>>>{});
  }

  Storage() {
    cache["row"].push_back(std::vector<std::unique_ptr<Base>>()); // To initialize with one empty vector
  }
};

当你调用 时,它将检查映射内最里面的向量是否为空。如果是,它会在将 推入向量之前添加一个空向量。在构造函数中,我们用一个空向量进行初始化。add_to_cacheunique_ptrcache["row"]

评论

0赞 undersilence 7/25/2023
感谢您的回答,但我的缓存类型是,它是否也适用于我的情况?std::map<std::string, std::vector<std::vector<std::unique_ptr<Base>>>>
0赞 Polarcode 7/25/2023
如果适合您,@undersilence查看更新的答案。
0赞 undersilence 7/26/2023
非常感谢,现有的检查工作对我来说。