【问题标题】:C++ standard list and default-constructible typesC++ 标准列表和默认可构造类型
【发布时间】:2010-10-16 06:33:25
【问题描述】:

为什么std::list<T> 的单参数构造函数要求T 是默认可构造类型?我的意思是以下代码无法编译。

struct Foo { // does not have default constructor.
  Foo (int i) {} 
}
int main(void) {
  std::list<Foo> l(10);
}

似乎可以使用construct and destroy idioms,就像他们已经在 std::vector 中所做的那样,尽管列表类有更多的簿记。

在相关说明中,为什么列表中没有容量功能?您可以争辩说,这样的功能会预先支付内存分配成本并在以后消除开销,因为您 push_back 对象。至少会让两个STL序列容器的接口稍微一致。

【问题讨论】:

  • std::vector 没有这样的限制。我的问题是为什么不在 std::list 中也使用相同的技术(创建/销毁习语)?
  • 为什么你删除了对我的回答的评论?
  • 我的评论有误。我不希望错误的信息四处飘荡。
  • 对于没有默认构造函数的类,可以将两个参数传递给列表和向量构造函数。原型实例作为第二个参数给出,它被克隆(使用复制构造函数)的次数与第一个参数一样多。如果您不提供原型实例,则剩下的唯一选项是默认构造函数。请参阅cplusplus.com/reference/stl/vector/vectorcplusplus.com/reference/stl/list/listpunchlet.wordpress.com/2009/12/03/letter-the-third/#comments

标签: c++ list stl default-constructor


【解决方案1】:

所以你的问题真的是“为什么列表中没有储备和容量功能?”

答案是没有理由提前为列表保留内存——添加新元素永远不需要对现有元素进行重新分配和复制,不要求保存列表内容的内存是连续的,并且迭代器执行list::push_back() 时不要失效。

所有这些都是vector&lt;&gt;::reserve() 存在的原因,而为新元素保留内存是vector&lt;&gt;new 放置到原始内存中的原因。

【讨论】:

    【解决方案2】:

    没有一般要求类型是默认可构造的 - 它必须是可复制和可分配的。您的代码不起作用,因为您尝试创建一个包含 10 个项目的列表——它们必须以某种方式构造,因此必须使用默认构造函数——但仅限于这种特定情况。如果您创建一个空列表并添加到其中,则不会有这样的要求。

    其他容器也是如此 - 尝试编译以下内容:

    #include <vector>
    
    struct A {
        A( int x ) : z(x) {}
        int z;
    };
    
    std::vector <A> a(10);
    

    关于您问题的第二部分,我只是观察到接口的一致性不是标准容器的主要设计标准 - 例如,无意将一种容器类型作为“插入式容器” “换另一个。 Scott Meyers 的“Effective STL”一书的第 1 项和第 2 项对此进行了很好的讨论。

    【讨论】:

    • 是的,std::vector 确实要求它们是默认可构造的(至少在您调用适当的构造函数/函数时)。如果我执行 std::vector v(5),如果 Foo 不是默认可构造的,v 应该包含什么?
    • 我收回我的话。 vector 对它的单参数构造函数确实有这个限制。对困惑感到抱歉。我想我的问题是为什么列表中没有储备和容量功能。
    • 我也喜欢 Neil 在代码 sn-p 之后的回答部分。我会重温 Scott Meyers 的书。
    【解决方案3】:

    为什么是单参数 std::list 的构造函数需要 T 成为默认可构造类型?

    因为这个构造函数 - 创建带有元素的列表(作为参数传递的数字)。每个元素的值将是默认值。您也可以使用带有两个参数的构造函数,用于创建包含将使用第二个元素值初始化的元素的列表。

    在相关说明中,为什么不使用 列表中的容量函数?

    这没有意义,因为向列表添加新元素的成本比使用矢量的情况要少得多。

    std::vector 没有这样的 限制。我的问题是为什么不 使用相同的技术 (创建/销毁成语)在 std::list 也是?

    这不是限制。因为如果您不使用此类构造函数,则不需要默认初始化程序。矢量也是如此。

    【讨论】:

      【解决方案4】:

      std::list 没有容量函数,因为它没有意义;它永远不必像矢量那样调整大小。它的容量只受可用内存的限制,不易确定。

      根据您的要求,我认为您实际上想要reserve()。这对于矢量来说是一次性的,因为它(非常)需要这样的东西;没有特别要求使所有 STL 容器中的所有功能保持一致,尤其是当它们对其他人毫无意义时。

      您可以使用自定义分配器实现相同的效果。正如 Manuel 建议的那样,看看 boost。

      【讨论】:

        【解决方案5】:

        Neil already answered the main question.

        另外请注意,调用 resize() 时需要一个默认构造函数。

        你可以通过一个指向对象的 STL 列表来规避这个问题,但我想这对你来说已经很明显了。

        在相关说明中,为什么不使用 列表中的容量函数?你可以 争辩说这样的功能会支付 内存分配成本预先和 以后消除开销 push_back 对象。至少会 制作两个STL的接口 排序容器稍微多一点 一致。

        我猜这里的问题是STL lists allow cross-list splicing。如果您想预先分配内存,请查看Boost Pool Allocator

        【讨论】:

          【解决方案6】:

          原因是,当您构造一个包含 n 个元素的列表(其中 n 是您在构造函数中使用的参数)时,该列表会用 T() 的副本填充其包含 n 个元素的结构。

          sgi stl documentation for list

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-07
            • 1970-01-01
            • 2012-04-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多