【问题标题】:Error when using std::make_move_iterator to insert a std::list<std::unique_ptr>> into another one使用 std::make_move_iterator 将 std::list<std::unique_ptr>> 插入另一个时出错
【发布时间】:2017-06-06 13:43:55
【问题描述】:

我一直在阅读 this answer 关于使用 std::make_move_iterator 将元素从 std::vector&lt;std::unique_ptr&lt;T&gt;&gt; 移动到另一个的内容。它完美无瑕:

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<_ty>::unique_ptr”:无法访问在类“std::unique_ptr<_ty>”中声明的私有成员

但是,如果列表包含其他对象(如 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()));

现在,如果我在同一个问题中使用another answer,它使用std::back_inserterstd::move,那么它同时适用于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::unique_ptrstd::list 上使用std::make_move_iterator 不起作用,但它对std::vector 起作用,或者如果列表元素属于不同类型?

注意:我使用的是 Visual Studio 2010。

【问题讨论】:

  • VS2010 很古老。 Works for me 与 VS2017。
  • 有趣...谢谢!
  • 我想知道它是否有任何解决方法?如问题中所述,我有另一个解决方案,但我暂时无法从 VS 2010 迁移
  • 要在列表之间移动,您可能应该使用splice
  • 不错的推荐,谢谢!正如你所说,它实际上更直接和自我记录

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


【解决方案1】:

核心问题是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&lt;std::unique_ptr&lt;T&gt;&gt; 附加到另一个的选项包括:

  • 使用back_inserter

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-07
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    相关资源
    最近更新 更多