如果我通过愚者4.7快照传递以下代码,它会尝试将unique_ptr
复制到vector中。
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
显然,这是行不通的,因为std::unique_ptr
是不可复制的:
错误:使用了已删除的函数'std::unique_ptr〈_Tp,_Dp〉::unique_ptr(const std::unique_ptr〈_Tp,_Dp〉&)[与_Tp = int;_Dp =标准::默认删除;标准::唯一指针〈_Tp,_Dp〉=标准::唯一指针]'
愚者尝试从初始化器列表中复制指针是否正确?
8条答案
按热度按时间chhqkbe11#
**编辑:**由于@Johannes似乎不想发布最佳解决方案作为答案,我就这么做了。
std::make_move_iterator
返回的迭代器将在取消引用时移动指向的元素。**原始答案:**我们将在此处使用一个小辅助类型:
遗憾的是,这里的直接代码不起作用:
由于标准,无论出于什么原因,都没有像下面这样定义转换复制构造函数:
由brace-init-list(
{...}
)创建的initializer_list<rref_wrapper<move_only>>
不会转换为vector<move_only>
所采用的initializer_list<move_only>
。euoag5mw2#
Python 18.9中
<initializer_list>
的概要相当清楚地表明,初始化器列表的元素总是通过const-reference传递的。不幸的是,在当前的语言版本中,似乎没有任何方法可以在初始化器列表元素中使用move-semantic。具体而言,我们有:
4uqofj5v3#
正如在其他答案中提到的,
std::initializer_list
的行为是通过值来保存对象,并且不允许移出,因此这是不可能的。下面是一个可能的解决方案,使用一个函数调用,其中初始化函数以可变参数的形式给出:不幸的是,
multi_emplace(foos, {});
失败了,因为它不能推导出{}
的类型,所以对于要默认构造的对象,您必须重复类名。(或者使用vector::resize
)ttvkxqim4#
std::make_move_iterator()
技巧和C20的std::to_array()
,你可以使用一个辅助函数,比如untomake_tuple()
等,这里称为make_vector()
:请在Godbolt上观看实况转播。
对于较早的C++,答案类似:
使用Johannes Schaub的技巧
std::make_move_iterator()
和std::experimental::make_array()
,您可以使用辅助函数:请在Coliru上观看实况转播。
也许有人可以利用
std::make_array()
的诡计让make_vector()
直接完成它的工作,但我不知道如何(更准确地说,我尝试了我认为应该工作的方法,失败了,然后继续)。在任何情况下,编译器都应该能够内联数组到向量的转换,就像Clang在GodBolt上使用O2所做的那样。frebpwbc5#
这是我最喜欢的解决方案。
C++17版本
C++11版本有点丑
wribegjk6#
这是一个为我们其他人提供简单扼要答案的尝试。
你不能。它坏了。
幸运的是,数组初始值设定项没有被破坏。
如果你想用这个数组初始化一个
std::vector<std::unique_ptr<T>>
,有无数种方法可以做到,其中很多都涉及到令人不快的模板元编程,所有这些都可以用for循环来避免。幸运的是,使用数组而不是std::vector在很多情况下都能正常工作,在这些情况下您确实更愿意使用std::vector。
或者,考虑写一个
custom::static_vector<T>
类,在初始化器列表中接受T*
,并在其析构函数中删除它们。同样不满意,但您需要接受这样一个事实,即std::vector<std::unique_ptr<T>>
在合理的时间内或付出合理的努力是无法工作的。您可以删除任何执行潜在移动的方法(move和copy构造函数,T&operator[]()
&c)。或者,如果您必须的话,您也可以实现一些基本的move语义(但您可能不需要)。见[1]的辩护,提供给成员的纯粹主义神职人员。
[1]编程语言应该提高生产率,而模板元编程在这种情况下并没有做到这一点。我所需要的只是一种方法,以确保我不会将静态初始化中分配的内存泄漏到堆中,从而无法使用valgrind来验证我没有泄漏内存。
这是一个日常的用例。而且这应该不难。把它变得很复杂只会带来捷径。
jdg4fx2g7#
为此,我编写了made a small library。
在www.example.com上运行gcc.godbolt.org
与
move_iterator
方法不同的是,这不需要移动每个元素。nullptr
直接放置到向量中,而不需要构建一个中间的std::unique_ptr
。这使得它甚至可以处理不可移动的类型:
acruukt98#
正如前面所指出的,用初始化器列表初始化只移动类型的向量是不可能的。最初由@Johannes提出的解决方案工作得很好,但我有另一个想法...如果我们不创建一个临时数组,然后将元素从那里移动到向量中,而是使用placement
new
来初始化这个数组,而不是向量的内存块,会怎么样?下面是我使用参数包初始化
unique_ptr
的向量的函数: