【问题标题】:"CopyConstructible" requirement for C++ stl container elementC++ stl 容器元素的“CopyConstructible”要求
【发布时间】:2011-09-25 19:21:30
【问题描述】:

关于C++ stl容器元素的要求,标准规定:元素类型应为CopyConstructible,并有CopyConstructible要求表。同样通过各种书籍(Josuttis 等),生成的副本应该与源“等效”。

我想我需要澄清一下。究竟什么是“等同于”?我也对“CopyConstructible”和“深/浅复制”之间的关系有点困惑。一般来说,拷贝构造函数要么是浅拷贝,要么是深拷贝。那么哪个适用于“CopyConstructible”,哪个不适用?

感谢任何cmets!

【问题讨论】:

  • 注意:本 Q/A 中提到的概念在 C++11 标准中发生了重大变化

标签: c++ stl containers copy-constructor deep-copy


【解决方案1】:

相关帖子:What is the difference between equality and equivalence?

已编辑以使其成为“非评论”。

等价的意思是“在所有意图和目的上都是平等的”。例如:1.0001 和 1 永远不相等,但在某些情况下它们是等价的。这是一般的答案。

书籍的意思是复制的对象必须满足strict weak ordering condition与原始对象:copy < original == false && original < copy == false

【讨论】:

  • 谢谢,Jörgen,我觉得这篇文章很有用!!我首先升级你的答案。
  • @Tomalak: 应该是一个标志 ;)
  • @Space:呵呵,是的,但我想给他一个机会在帖子被删除之前保存他的帖子。 :)
  • 严格的弱排序与等价有什么关系?拥有一个没有排序关系的对象集合(例如向量、双端队列或列表)是完全合法的。
  • @Charles:他没有提到他一直在阅读哪些容器,但我假设它是常见的嫌疑人std::mapstd::set。我的意思是,std::vector 一开始就不会提出这个问题...?
【解决方案2】:

深拷贝或浅拷贝都可以。例如,shared_ptr 总是做一个浅拷贝(带有一些额外的引用计数的东西),你可以在容器中使用它们就好了。这取决于复制操作的语义。

等效意味着您的程序不应依赖于它是否适用于原件或副本。

【讨论】:

  • 嗨,Space_C0wb0y,感谢您的回答。 shared_ptr 是浅拷贝的一种特殊情况。我的意思是它不仅使用浅拷贝,还使用引用计数。如果我们谈论一个虚拟的浅拷贝,它是否满足“CopyConstructible”?
  • 所以你的意思是,这两个概念之间没有关系或暗示。
  • 我的意思是,没有规则禁止在标准容器中使用具有浅拷贝语义的类。
  • 谢谢,Space_C0wb0y,这是一个很好的例子。我选择接受你的回答。感谢所有其他非常有用的 cmets。我会分别投票。
【解决方案3】:

如果您将某些东西放入容器中,当您检索它时,您将得到与您放入的东西等效的东西。只要这对您的对象有意义,那么您就会得到一些东西从容器中有用。

这是浅拷贝还是深拷贝取决于您想要的对象类型的语义。你的对象可能是指针式的、句柄式的或者可能是容器式的。它可能包含一些可变缓存数据,您可能会或可能不会选择在复制操作中复制这些数据。

只要您的复制构造函数是可访问的,并且执行您需要它执行的操作以保留对象类型的语义,那么您就满足 CopyConstructible 要求。

【讨论】:

  • 嗨,查尔斯,我先对你的答案投赞成票。我明白了。我觉得我有点误导。如果使用得当,浅拷贝或深拷贝都可以保留语义。例如,对于 POD(纯对象数据)或没有动态内存分配的类,浅拷贝可以很好地进行拷贝构造。我说的对吗?
  • @pepero:这取决于对象。通常一个 POD 结构满足 CopyConstructible 但一个 POD 结构可能包含指针,如果它应该“由指针拥有”(这是糟糕的设计 - 它不应该再是一个 POD)然后它不是 CopyConstructible
  • 好的,但是在这种情况下,内部包含指针的设计不佳的 pod 不应该使用浅拷贝。对吗?
  • @pepero:POD 必须使用“浅拷贝”,因为这就是 POD 的复制方式。为了使它做一个合理的复制,它需要一个用户定义的复制构造函数,这意味着它不能再是 POD。
【解决方案4】:

认为“复制构造函数执行深复制或浅复制”有点局限,而且有点像红鲱鱼。

虽然确实,根据您的对象存储为成员的内容,您可能需要进行一些深度复制以获得等效性,但就类型的 接口 而言,它不会如何您执行复制并不重要...只要您确实执行了复制并且您最终得到了一个等效的对象。

如果A 等价于B,那么对于设计合理的类型,A==B

整个要求只是说:“元素类型必须是可复制的”。其余的都归结为编写适当的复制构造函数的通常常识。

【讨论】:

  • 如果 A == B,则 A 和 B 相等。从本质上讲,这意味着它们也是等价的。但也有 A 和 B 等价但不等价的情况。如果 A
  • @Jörgen:正确;这是一个更准确的描述。
【解决方案5】:

一般来说,STL 容器可能会在某个阶段,在某些操作或算法期间复制您的元素,因此试金石是:

Element original(....);  // construct this awesome object
original.this_and_that();  // do stuff to it until the value is perfect...

Element another(original);

你能愉快地使用another而不是original吗?

这实际上是 CopyConstructible 要求所说的:您最好能够将其复制到另一个对象中并且仍然对结果感到满意。这不是一个严格的限制——你只需要考虑清楚并相应地编写你的复制构造函数。

但是,重要的是,像find() 这样的一些操作可能会使用== 来比较元素(对于其他容器,它可能是'如果被复制的副作用是您无法有意义地比较元素,那么您的 finds 等人可能会停止工作 - 也要考虑清楚! (标准对容器说,“== 是一种等价关系”(23.1-5)。)

【讨论】:

    【解决方案6】:

    这里讨论了几个不同的概念。 CopyConstructible 只要求您可以使用现有元素作为复制源来创建该类型的新元素。它与 deepshallow 甚至 equivalence 无关:只要允许容器复制,容器不关心您在做什么执行该副本。

    第二个概念是等价的概念,当你在容器中使用一个对象时,它会被复制,并且执行的复制数量是未知的——实现可能只复制一次到将其存储在内部,或者它可能会在内部制作多个副本。 想要的是能够从容器中提取元素并像原始对象一样使用它,这就是 equivalence 的用武之地:第 n 个您提取的副本应该等同于您插入的对象。

    deepshallow 复制的概念与 等价 直接相关,取决于您正在建模的领域,等价可能需要shallowdeep 复制,并且根据其他限制,您可能必须选择一个或另一个 - 只要在您的域中它们是等效的 - 或者甚至可能是中间选项,其中执行部分深度复制,或者根本不复制某些成员。

    【讨论】:

    • CopyConstructible 确实明确要求副本是等效的,它在标准的相关表格中说了这么多,只是没有详细说明等效的含义任意类型。
    猜你喜欢
    • 1970-01-01
    • 2012-11-14
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 2015-06-06
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    相关资源
    最近更新 更多