2011-12-11 88 views
11

比方说,我有带有const成员的结构向量?

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

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

+0

这似乎是编译并与VC 2010链接。您能提供有关您的环境的更多信息吗?这是一个完整的测试用例,是否会再现编译失败? – DRH

+0

@DRH:我在VC 2008上,对不起。是的,这是整个测试案例。 – Mehrdad

+0

虽然对于其他操作,您将需要赋值运算符,但我想不出任何可能的原因,为什么'push_back'会有这个要求......然后,可能是实现检查'Assignable'要求。 –

回答

7

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

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

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

从表64(Assignable要求):

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

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

在理论上,std::vector相当于可以选择做破坏,在所有情况下拷贝构造,但是这不是已经选择了合同。如果不需要重新分配,那么对于像vector::operator=vector::assign这样的东西使用包含类型的赋值运算符可能会更有效。

+0

呵呵......所以即使我从不指定任何东西,它也需要赋值。不知道。 – Mehrdad

+0

@Philipp:实际上,重新分配时,矢量不分配任何东西。它通常只复制或移动构建新范围,然后删除旧范围。 –

+0

嗯......为什么'vector'执行拷贝分配呢?难道它不能复制施工+破坏,而不是使用'operator ='? – Mehrdad

8

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

的选项有:

  1. nameconst
  2. 写下您自己的复制分配操作员,并想办法处理“复制”const成员。
1

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

3

A vector通常需要移动元素。每当调用push_back()时向量需要增长时,它会重新分配内存以保持自身连续性,并将所有现有元素复制到新空间中。另外,如果您拨打insert()remove()元素,则必须移动 。对于vector能够做所有元素必须是可复制分配的,这意味着您存储在向量中的类型必须具有已定义的赋值运算符。

通常,如果您定义了一个类,编译器将为您生成该类的赋值运算符。但是,有些情况下编译器无法做到这一点。其中一种情况是类有常量成员(请注意,指向常量的指针可以)。

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

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

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