我想在一个向量中存储不同类型的unique_ptrs。
我尝试使用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));
}
其失败原因如下。
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)
|
这可以用std::any实现吗?或者有其他的选择吗?我不能用std::variant,因为我不知道所有要预先存储的类型。
编辑:
我只想使用unique_ptr向量来强制对象在程序退出时被清除(向量在程序退出前一直有效),消费者代码将使用对a和b的直接引用,因此我不需要通过向量访问/枚举对象引用。
3条答案
按热度按时间wn9m85ua1#
std::unique_ptr
不能用作std::any
,因为后者要求值类型是可复制构造的,而std::unique_ptr
不是。根据您描述的使用情形:
直接的解决方案是使用
std::shared_ptr
,它是可复制构造的。然而,在这种情况下,
std::any
就不是必需的了。所有的std::shared_ptr
示例总是可以转换成std::shared_ptr<void>
。删除器是类型擦除的,仍然会被调用:需要将元素直接构造为
shared_ptr
(例如,使用std::make_shared
),否则new
表达式和shared_ptr
构造之间的异常将导致内存泄漏。std::move
是可选的。然而,
std::shared_ptr
比你真正需要的要多得多。std::unique_ptr
没有一个void
示例可以使用,因为删除器的类型没有被擦除,但是你可以通过写一个std::unique_ptr
等价物来达到同样的效果,这个等价物是从一个带有虚析构函数的非模板基类派生出来的,然后你可以把那个基类作为你的向量中的元素类型。(If您可以使用额外的间接寻址,这可以通过在派生模板中使用
std::unique_ptr
本身,然后在基类中使用std::unique_ptr<base>
来轻松实现。如果您不需要间接寻址,那么我认为您必须从头开始实现它。我想不出有什么有用的标准库功能。)你也可以实现你自己的
std::any
等价物来支持不可复制的类型。std::any
只是简单地决定支持复制,一旦决定了,所有可能包含的类型都必须支持它。但是这比我上面建议的要复杂得多。然而,一个更简单的解决方案,假设设计和性能对你来说是可接受的,就是让你想要存储的所有类
A
,B
等等都是用虚析构函数从某个Base
派生出来的,在这种情况下,std::vector<std::unique_ptr<Base>>
也会这样做。但是在这种情况下,虚析构函数是必需的!否则你将有未定义的行为!pb3s4cty2#
如果你只需要它来销毁对象,那么你可以做一些简单得多的事情,例如:
wb1gzix03#
除了其他人写的,我还要说在
是反模式或代码味道。
这是因为
std::any
示例与智能指针示例类似,将接管其所持有的对象的唯一所有权,而智能指针仅接管指针的所有权。但是,如果复制std::any
示例,则与std::unique_ptr
示例不同,它将希望复制其所持有的对象。因此,我只是先尝试执行this,一个std::any
示例将执行destroy the object it holds,所以在这方面不需要智能指针。您的应用程序设计可能会得到改进,但是编写一个与标准
std::any
行为不同的定制std::any
(例如,您可以禁止复制或实现共享所有权)是一个值得的项目,因为类型擦除是c++中反复出现的主题。最后,如果担心内存泄漏,可以使用memory sanitizers。