对 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 的原始代码修改为 move
和 unique_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比原来的版本好多了,抱歉。
以下代码将无法编译:
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 的原始代码修改为 move
和 unique_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比原来的版本好多了,抱歉。