【问题标题】:Vector of structs with const members?具有 const 成员的结构向量?
【发布时间】:2011-12-11 22:17:28
【问题描述】:

假设我有

#include <string>
#include <vector>
using namespace std;

struct Student
{
    const string name;
    int grade;
    Student(const string &name) : name(name) { }
};

那么,我该如何保留学生的向量?

int main()
{
    vector<Student> v;

    // error C2582: 'operator =' function is unavailable in 'Student'
    v.push_back(Student("john"));
}

有没有办法做到这一点,或者我必须在堆上分配所有学生,并改为存储指向每个学生的指针?

【问题讨论】:

  • 这似乎与 VC 2010 进行编译和链接。您能否提供有关您的环境的更多信息?这是一个重现编译失败的完整测试用例吗?
  • @DRH:我在 VC 2008 上,抱歉。是的,这就是整个测试用例。
  • 虽然对于其他操作,您需要赋值运算符,但我想不出push_back 有这个要求的任何可能原因......再说一次,可能是实现检查了@987654324 @要求。

标签: c++ constants c++03


【解决方案1】:

你不能。您的类型违反了标准容器的“可分配”要求。

ISO/IEC 14882:2003 23.1 [lib.container.requirements] / 3:

这些组件中存储的对象类型必须满足CopyConstructible的要求 types (20.1.3),以及Assignable types 的附加要求。

来自表 64(Assignable 要求):

在表 64 中,T 是用于实例化容器的类型,tT 的值,u 是(可能是 constT 的值。

表达式:t = u;返回类型:T;后置条件:t 等价于u

理论上,std::vector 等效项在所有情况下都可以选择进行破坏和复制构造,但这不是已选择的合同。如果不需要重新分配,则对 vector::operator=vector::assign 等内容使用包含类型的赋值运算符可能会更有效。

【讨论】:

  • 哦哈……所以即使我从不分配任何东西,它也需要可分配?不知道。
  • @Philipp:实际上,向量在重新分配时没有分配任何东西。它通常只复制或移动构建新范围,然后删除旧范围。
  • 嗯...为什么vector 执行复制分配?它不能复制构造+破坏,而不是使用operator =吗?
  • @OliCharlesworth:为什么insert 需要赋值呢?我认为它需要一个复制构造函数(使用allocator::allocate),因为它不是分配给一个有效的对象,而是分配给一个新分配的、未格式化的内存块,对吧?
  • @Mehrdad:因为在伪代码中,它会做类似:for (int i = size(); i &gt; pos; i--) { elements[i] = elements[i-1]; }
【解决方案2】:

简单的答案是:你不能。如果您有 const 成员变量,则编译器无法提供默认的复制赋值运算符。但是,std::vector 提供的许多操作都需要进行赋值,因此需要(公共)复制赋值运算符。

您的选择是:

  1. 使nameconst
  2. 编写您自己的复制赋值运算符,并想办法处理“复制”const 成员的问题。

【讨论】:

    【解决方案3】:

    vector 经常需要移动元素。每次当您调用push_back() 时,向量需要增长时,它都会重新分配内存以保持自身连续,并将所有现有元素复制到新空间中。此外,如果您调用 insert()remove() 元素必须 被转移。要让vector 能够完成所有元素必须是可复制赋值的,这意味着您存储在向量中的类型必须定义赋值运算符。

    一般来说,如果你定义了一个类,编译器会为你生成那个类的赋值运算符。但是,有些情况下编译器无法做到这一点。其中一种情况是当类具有常量成员时(请注意指向常量的指针是可以的)。

    因此,就您而言,问题在于const string name。它会阻止编译器生成operator=(),从而阻止vector 编译,即使您自己实际上并未对其元素使用赋值。

    一种解决方案是使name 非常量。另一种是写你自己的Student::operator=(),在某种程度上是有意义的。正如您所指出的,第三种方法是使用指针向量而不是对象向量。但是你必须处理它们的分配和取消分配。

    附:编译器无法生成 operator= 的另一种情况是您的类具有引用的成员。

    【讨论】:

      【解决方案4】:

      向量的元素必须是可复制分配的,您的Student 结构不是因为const 成员。只需使用string name 而不是const string name。 除非您有特定要求,否则类中的常量成员很少有用。如果您想阻止更改成员,请将其设为私有并添加公共 getter 函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-02
        • 1970-01-01
        • 2012-12-27
        • 1970-01-01
        • 2017-02-10
        • 1970-01-01
        • 1970-01-01
        • 2012-12-12
        相关资源
        最近更新 更多