13

假设我有

#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"));
}

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

4

4 回答 4

9

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

您的选择是:

  1. 使nameconst.
  2. 编写您自己的复制赋值运算符,并想出一种处理“复制”const成员的方法。
于 2011-12-11T22:20:02.757 回答
9

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

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

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

来自表 64(Assignable要求):

在表 64 中,T是用于实例化容器的类型,t是 的值T,并且u是(可能const)的值T

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

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

于 2011-12-11T22:20:08.233 回答
6

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

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

所以,在你的情况下,问题是const string name. 它会阻止编译器生成operator=(),这反过来又会阻止vector编译,即使您自己实际上并未对其元素使用赋值。

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

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

于 2011-12-11T22:40:13.393 回答
1

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

于 2011-12-11T22:21:08.263 回答