对 unique_ptrs 的列表进行排序

Sorting a list of unique_ptrs

以下代码将无法编译:

bool ptrLess(unique_ptr<int> ptr1, unique_ptr<int> ptr2)
{
   return *ptr1 < *ptr2;
}

int main()
{
   unique_ptr<int> ptr1(new int(3));
   unique_ptr<int> ptr2(new int(2));
   unique_ptr<int> ptr3(new int(5));
   list<unique_ptr<int>> list;

   list.push_back(ptr1);
   list.push_back(ptr2);
   list.push_back(ptr3);

   list.sort(ptrLess);

   for (auto &element : list) {
      cout << *element;
   }

   return 0;
}

我认为这是因为 unique_ptr 的复制构造函数被删除了。我收到如下错误:

error C2280: 'std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function

有没有什么方法可以对 unique_ptr 的列表进行排序,或许可以使用移动构造函数来代替?

你应该使用 const ref - 毕竟你不想修改那些指针:

bool ptrLess(const unique_ptr<int>& ptr1, const unique_ptr<int>& ptr2)

如果您的 list 模板是 std::list,则将参数作为右值引用传递将不起作用 - list::sort 将不得不调用 std::move 有效地重置您的指针。

编辑

至于列出你的代码的其余部分:std::list 有一个方便的方法叫做 emplace_back(和 emplace_front),它允许你就地构造和附加一个元素:

your_list.emplace_back(new int(2));

尝试通过 const ref 传递,这样它就不会复制参数: bool ptrLess(const unique_ptr& ptr1, const unique_ptr& ptr2) { return *ptr1 < *ptr2; }

试试这个:

#include <memory>
#include <list>
#include <iostream>
using namespace ::std;

bool ptrLess(unique_ptr<int>& ptr1, unique_ptr<int>& ptr2)
{
   return *ptr1 < *ptr2;
}

int main()
{
   unique_ptr<int> ptr1(new int(3));
   unique_ptr<int> ptr2(new int(2));
   unique_ptr<int> ptr3(new int(5));
   list<unique_ptr<int>> list;

   list.push_back(move(ptr1));
   list.push_back(move(ptr2));
   list.push_back(move(ptr3));

   list.sort(ptrLess);

   for (auto &element : list) {
      cout << *element;
   }

   return 0;
}

这里的问题是您需要了解 unique_ptr 的实际目标是什么:

在处理 pointers/references 时,如果有多个 pointersrs/references 引用同一个对象,就会出现大量潜在问题。 unique_ptr 试图避免这种情况。 因此,您不能创建 2 个 unique_ptr 引用同一个对象。

您不能使用您的 ptrLess() 函数,因为它的调用方式类似于

   unique_ptr<int> ptr1(new int(3));
   unique_ptr<int> ptr2(new int(2));

   ptrLess(ptr1, ptr2);

因为这意味着 ptr1 必须复制一个 ptr2 并传递给 ptrLess() - 这里的关键字是 'call-by-value'.

而且,你不能做

   list<unique_ptr<int>> list;
   unique_ptr<int> ptr1(new int(3));

   unique_ptr<int> ptr1(new int(3));

因为这也是,必须创建 ptr1 的副本。 这里的解决方案是不将 unique_ptr 作为值传递给 ptrLess,而是作为参考:

bool ptrLess(unique_ptr<int>& ptr1, unique_ptr<int>& ptr2);

并且您没有将副本传递到列表中,但是将您的对象移到那里:

list.push_back(move(ptr1));

这里的关键词是'move-semantics'。 这将使您的 ptr1 变量的内容无效 - 对象已从 ptr1 移入列表。

如果您对这些更感兴趣,我建议您看看 Rust 语言 ;)

正如 Baum mit Augen 指出的那样,ptrLess 的参数最好声明为 const:

bool ptrLess(const unique_ptr<int>& ptr1, const unique_ptr<int>& ptr2);

我突然想到,如果 OP 使用 shared_ptr 而不是 unique_ptr,那么最初 posted 的代码将 运行 不变:

#include <memory>
#include <list>
#include <iostream>
using namespace ::std;

bool ptrLess(const shared_ptr<int>& ptr1, const shared_ptr<int>&  ptr2)
{
   return *ptr1 < *ptr2;
}

int main()
{
   shared_ptr<int> ptr1(new int(3));
   shared_ptr<int> ptr2(new int(2));
   shared_ptr<int> ptr3(new int(5));
   list<const shared_ptr<int>> list;

   list.push_back(ptr1);
   list.push_back(ptr2);
   list.push_back(ptr3);

   list.sort(ptrLess);

   for (auto &element : list) {
      cout << *element;
   }

   return 0;
}

运行 它在 Wandbox.

从某种意义上说,这是一种一致的做事方式。 push_back 通常会复制要添加到列表中的对象,如果调用者想要使用它,则原始对象仍然可供调用者使用。使用 shared_ptr 具有相似的语义,而无需复制对象本身的开销。相反,仅复制 shared_ptr,这是一种廉价操作。

此外,将 OP 的原始代码修改为 moveunique_ptrs 到列表中本质上是脆弱的。它们仍然在调用者的范围内,但不再可用。如果你尝试,你会得到(我假设)一个 nullptr 取消引用。那么,这样做更好(注意额外的一组大括号):

...

list<unique_ptr<int>> list;

{
   unique_ptr<int> ptr1(new int(3));
   unique_ptr<int> ptr2(new int(2));
   unique_ptr<int> ptr3(new int(5));

   list.push_back(move(ptr1));
   list.push_back(move(ptr2));
   list.push_back(move(ptr3));
}

...

现在你安全了。

好的,post比原来的版本好多了,抱歉。