使用std :: make_move_iterator插入std :: list <std :: unique_ptr>&gt;时出错进入另一个

时间:2017-06-06 13:43:56

标签: c++11 linked-list insert move-semantics unique-ptr

我一直在阅读this answer关于使用std::vector<std::unique_ptr<T>>将元素从std::make_move_iterator移动到另一个元素的问题。它完美无缺:

std::vector<std::unique_ptr<int>> c1;
std::vector<std::unique_ptr<int>> c2;
c1.push_back(std::unique_ptr<int>(new int));
c2.insert(c2.end(), std::make_move_iterator(c1.begin()), std::make_move_iterator(c1.end()));

现在我尝试将其更改为使用其他容器(std::list):

std::list<std::unique_ptr<int>> c1;
std::list<std::unique_ptr<int>> c2;
c1.push_back(std::unique_ptr<int>(new int));
c2.insert(c2.end(), std::make_move_iterator(c1.begin()), std::make_move_iterator(c1.end()));

但编译失败了:

  

错误C2248:'std :: unique_ptr&lt; _Ty&gt; :: unique_ptr':无法访问类'std :: unique_ptr&lt; _Ty&gt;'中声明的私有成员

但是,如果列表包含其他对象(如std::string):

,它确实有效
std::list<std::string> s1;
std::list<std::string> s2;
s2.insert(s2.end(), std::make_move_iterator(s1.begin()), std::make_move_iterator(s1.end()));

现在,如果我使用std::back_inserterstd::move同一问题中的another answer,则它同时适用于std::vectorstd::list

std::list<std::unique_ptr<int>> c1;
std::list<std::unique_ptr<int>> c2;
c1.push_back(std::unique_ptr<int>(new int));
std::move(c1.begin(), c1.end(), std::back_inserter(c2));

据我所知,这个解决方案基本上是在第二个容器上插回时单独移动每个项目。

我的问题是,为什么在std::make_move_iterator std::list std::unique_ptr上使用std::vector不起作用,但是{{1}}或者列表元素属于不同类型?

注意:我正在使用Visual Studio 2010。

1 个答案:

答案 0 :(得分:0)

核心问题是Visual Studio 2010的std::list::insert实现并不支持移动语义。在内部,std::list::insert调用一个名为_Insert的方法,在VS 2010中声明如下:

void _Insert(const_iterator _Where, const _Ty& _Val)

在VS 2017中(未检查其他版本),它调用相同的方法,但声明为:

void _Insert(_Unchecked_const_iterator _Where, _Valty&&... _Val)

如图所示,VS 2017使用右值引用,因此在插入时调用std::unique_ptr的移动构造函数。另一方面,VS 2010使用左值引用,因此在某些时候调用复制构造函数,该函数被std::unique_ptr声明为私有,从而生成编译错误。

<强>结论

在VS 2010中,无法使用基于std::make_move_iterator的解决方案,因为std::list::insert是如何实现的。将std::list<std::unique_ptr<T>>附加到另一个中的选项包括:

  • 使用back_inserter

    std::move(c1.begin(), c1.end(), std::back_inserter(c2));
    
  • 使用std::list::splice(归功于@T.C.),这非常有用,因为它可以直接用于函数返回的列表:

    c2.splice(c2.end(), c1);