使用 std::vector<std::any> 存储异构的 unique_ptrs

cat*_*una 5 c++ c++20

我想将不同类型的 unique_ptr 存储在向量中。

我尝试按如下方式使用 std::any 。

#include <vector>
#include <any>
#include <memory>

class A
{
   int val;
};

class B
{
   float val;
};


int main()
{
    std::vector<std::any> vec;
    
    auto a = new A();
    auto b = new B();
    
    vec.push_back(std::unique_ptr<A>(a));
    vec.push_back(std::unique_ptr<B>(b));
}
Run Code Online (Sandbox Code Playgroud)

它失败如下。

main.cpp: In function 'int main()':
main.cpp:23:18: error: no matching function for call to 'std::vector<std::any>::push_back(std::unique_ptr<A>)'
   23 |     vec.push_back(std::unique_ptr<A>(a));
      |     ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/local/include/c++/12.1.0/vector:64,
                 from main.cpp:1:
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
 1276 |       push_back(const value_type& __x)
      |       ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:35: note:   no known conversion for argument 1 from 'std::unique_ptr<A>' to 'const std::vector<std::any>::value_type&' {aka 'const std::any&'}
 1276 |       push_back(const value_type& __x)
      |                 ~~~~~~~~~~~~~~~~~~^~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
 1293 |       push_back(value_type&& __x)
      |       ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:30: note:   no known conversion for argument 1 from 'std::unique_ptr<A>' to 'std::vector<std::any>::value_type&&' {aka 'std::any&&'}
 1293 |       push_back(value_type&& __x)
      |                 ~~~~~~~~~~~~~^~~
main.cpp:24:18: error: no matching function for call to 'std::vector<std::any>::push_back(std::unique_ptr<B>)'
   24 |     vec.push_back(std::unique_ptr<B>(b));
      |     ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
 1276 |       push_back(const value_type& __x)
      |       ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:35: note:   no known conversion for argument 1 from 'std::unique_ptr<B>' to 'const std::vector<std::any>::value_type&' {aka 'const std::any&'}
 1276 |       push_back(const value_type& __x)
      |                 ~~~~~~~~~~~~~~~~~~^~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
 1293 |       push_back(value_type&& __x)
      |       ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:30: note:   no known conversion for argument 1 from 'std::unique_ptr<B>' to 'std::vector<std::any>::value_type&&' {aka 'std::any&&'}
 1293 |       push_back(value_type&& __x)
      |              
Run Code Online (Sandbox Code Playgroud)

这对于 std::any 可能吗?或者有什么替代方案吗?我无法使用 std::variant 因为我不知道要预先存储的所有类型。

编辑

我只想使用 unique_ptr 向量强制在程序退出时清理对象(该向量将一直存在直到程序退出)。消费者代码将使用对 a 和 b 的直接引用,因此我不需要通过向量访问/枚举对象引用。

use*_*522 8

std::unique_ptr不能用作 a std::any,因为后者要求值类型是可复制构造的,而事实std::unique_ptr并非如此。


鉴于您描述的用例:

一个直接的解决方案是使用可std::shared_ptr复制构造的替代方案。

然而,在这种情况下std::any根本没有必要。所有std::shared_ptr实例始终可以转换为std::shared_ptr<void>. 删除器已被类型擦除,并且仍将按预期调用:

std::vector<std::shared_ptr<void>> vec;

auto new_a = std::make_shared<A>();
A* a = new_a.get();
vec.push_back(std::move(new_a));

auto new_b = std::make_shared<B>();
B* b = new_b.get();
vec.push_back(std::move(new_b));

// use a and b here, assuming that vec outlives them
Run Code Online (Sandbox Code Playgroud)

该元素需要直接构造成 a shared_ptr(例如 with ),因为否则表达式和构造std::make_shared之间的异常将导致内存泄漏。是可选的。newshared_ptrstd::move

然而,std::shared_ptr这比您真正需要的要多得多。std::unique_ptr没有void可以使用的实例,因为删除器的类型不会被删除,但是您可以通过编写std::unique_ptr从具有虚拟析构函数的单个非模板基类派生的等效项来实现相同的效果。然后您可以使用该基类作为向量中的元素类型。

(如果您对附加间接寻址感到满意,则可以通过std::unique_ptr在派生模板中使用自身然后std::unique_ptr<base>在基类中使用来轻松实现。如果您不想要间接寻址,那么我认为您必须从从头开始。我想不出有用的标准库功能。)


还可以实现您自己的std::any支持不可复制类型的等效项。std::any只是做出了支持复制的决定,一旦做出该决定,所有可能包含的类型都必须支持它。但这比我上面建议的更复杂。


然而,一个更简单的解决方案,假设这对您来说是可以接受的设计和性能,是让您打算存储的所有类 、 等都从具有虚拟析构函数的类派生A,在这种情况下也可以这样做。然而在这种情况下,需要虚拟析构函数!否则你将会有未定义的行为!BBasestd::vector<std::unique_ptr<Base>>