为什么将 std::auto_ptr<> 与标准容器一起使用是错误的?

Why is it wrong to use std::auto_ptr<> with standard containers?

提问人:Uhall 提问时间:9/22/2008 最后编辑:Lightness Races in OrbitUhall 更新时间:3/31/2017 访问量:49578

问:

为什么与标准容器一起使用是错误的?std::auto_ptr<>

C stl raii auto-ptr c++-faq

评论

6赞 twokats 11/8/2008
绝对是+1,因为我见过很多人弄错了。这是一个很好的问题。
0赞 nickolay 12/29/2011
另请阅读相关项目。这个问题是从另一个方面考虑的。可能有助于了解有关 auto_ptr 和 STL 容器的更多信息。stackoverflow.com/questions/8630552/......
0赞 Amro 5/18/2012
C++ 常见问题解答:是否可以有一个指向对象的智能指针容器?
1赞 Phil1970 5/12/2017
move语义,旨在避免与 相关的问题。在 C++ 03 中,该语言不够强大,无法编写在所有场景中都正确安全地运行的类,因为编译器和语言无法区分 l 和 r 值,因此大多数时候使用一些“hack”来获得所需的行为。unique_ptrauto_ptrauto_ptr
0赞 alfC 6/8/2020
好文章: STL 容器和Auto_ptrs - 为什么它们不混合 quantstart.com/articles/...

答:

67赞 Frank Krueger 9/22/2008 #1

复制语义与容器不兼容。auto_ptr

具体来说,将一个对象复制到另一个对象不会创建两个相等的对象,因为其中一个对象已经失去了对指针的所有权。auto_ptr

更具体地说,复制会导致其中一个副本放弃指针。哪些保留在容器中未定义。因此,如果存储在容器中,则可能会随机失去对指针的访问权限。auto_ptrauto_ptrs

17赞 Garth Gilmour 9/22/2008 #2

STL 容器需要能够复制存储在其中的项目,并且设计为期望原始容器和副本是等效的。自动指针对象具有完全不同的约定,通过该约定,复制将创建所有权转移。这意味着auto_ptr容器将表现出奇怪的行为,具体取决于使用情况。

在有效 STL (Scott Meyers) 第 8 项中有关于可能出错的详细描述,在有效 C++ (Scott Meyers) 第 13 项中也有不太详细的描述。

12赞 Dustin Getz 9/22/2008 #3

STL 容器存储所包含项目的副本。复制auto_ptr时,它会将旧的 ptr 设置为 null。许多容器方法都因此行为而中断。

评论

0赞 Tracer 7/11/2014
但是,当使用unique_ptr时,你会得到几乎相同的东西,因为只有一个unique_ptr可以拥有对象的所有权?
2赞 underscore_d 2/23/2016
@Tracer像任何适当的 C++11 对象一样,只能在移动构造或分配时转移其资源的所有权,确保程序员必须故意传递 或 临时,而不是传递左值和不直观/不可预测的方式让它因复制赋值而发生变异......正如这里所强调的,这是 的核心问题。unique_ptrstd::move(sourceObject)auto_ptr
126赞 6 revs, 5 users 80%Kevin #4

C++标准规定STL元素必须是“可复制构造的”和“可分配的”。换句话说,一个元素必须能够被赋值或复制,并且这两个元素在逻辑上是独立的。 不满足此要求。std::auto_ptr

以以下代码为例:

class X
{
};

std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);

std::auto_ptr<X> pX = vecX[0];  // vecX[0] is assigned NULL.

若要克服此限制,应使用 std::unique_ptr、std::shared_ptr 或 std:weak_ptr 智能指针,如果没有 C++11,则应使用 boost 等效指针。以下是这些智能指针的 boost 库文档。

评论

7赞 me22 9/11/2009
如果不需要共享所有权,还应考虑提升指针容器。
5赞 Mike Weller 7/1/2013
unique_ptr还不允许复制,因此某些 STL 操作将无法正常工作,除非它们可以使用其移动语义。
4赞 Marc van Leeuwen 8/27/2014
“为了克服这个限制,你应该使用”:该类模板只能因为移动语义而存在(它的规范需要右值引用),所以它从根本上需要C++11。但是(和相关的)C++11 标准不再说 STL 元素类型必须是“可复制构造的”和“可分配的”;可移动构造和可移动分配就足够了。事实上,实例只能是可移动构造和可移动分配的。但实例也是如此!因此,在 C++11 中,你可以用 .std::unique_ptrunique_ptrauto_ptrauto_ptrunique_ptr
0赞 ratchet freak 9/3/2014
@MarcvanLeeuwen除非您和需要resetrelease
2赞 Marc van Leeuwen 9/3/2014
@ratchetfreak:嗯,我不明白。什么?“除非你和”,我看不出这如何适用于我评论中的任何内容。请注意,两者都具有这两种方法,并且在两种情况下它们都执行相同的操作。resetreleaseauto_ptrunique_ptr
39赞 Lazer 6/26/2010 #5

关于该主题的两篇超级优秀的文章:

评论

0赞 Puppy 6/26/2010
因为我认为,在将近两年的时间里,他可能处理了手头的问题。
28赞 Lazer 6/26/2010
@DeadMG:是的,你是对的。但那不是我的目的。我敢肯定,如果有人某个时候来到这个线程并想了解这些东西,这些链接会有所帮助。auto_ptr
0赞 Puppy 6/26/2010
有很多最近的重复项。
8赞 Sebastian Mach 11/19/2011
@DeadMG:这个问题没有作为重复问题关闭,因此可以扩展。拉泽尔在这里说了之前没有说过的话。我猜他是偶然过来的。
0赞 chaosink 5/2/2020
第二个链接中的解释,分析了调用后的问题,比这里的所有答案都清楚。sort()
4赞 Alex Bitek 10/30/2012 #6

C++ 标准 (ISO-IEC 14882-2003) 在第 20.4.5 条第 3 款中说:

[...] [注意:[...] auto_ptr 不符合标准库的 CopyConstructible 和 Assignable 要求 容器元素,从而实例化标准库容器 auto_ptr会导致未定义的行为。— 结束语]

C++ 标准 (ISO-IEC 14882-2011) 在附录 D.10.1 第 3 段中说:

[...]注意:[...]auto_ptr实例满足以下要求 MoveConstructible 和 MoveAssignable,但不符合要求 的 CopyConstructible 和 CopyAssignable。— 尾注 ]

C++14标准(ISO-IEC 14882-2014)在附录C.4.2中说 附件D:兼容性特点:

更改:类模板 auto_ptr、unary_function 和 binary_function,函数模板random_shuffle,以及 函数模板(及其返回类型) ptr_fun、mem_fun、 mem_fun_ref、bind1st 和 bind2nd 未定义。
基本原理:被新功能取代。
对原始功能的影响:使用这些类模板和函数模板的有效 C++ 2014 代码可能无法在此编译 国际标准。