Вектор структур с константными членами?
Скажем, у меня
#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"));
}
Есть ли способ сделать это, или я должен выделить всех студентов в куче и вместо этого сохранить указатель на каждый из них?
Ответы
Ответ 1
Вы не можете. Ваш тип нарушает требование "Назначение" для стандартных контейнеров.
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
Теоретически эквивалент a std::vector
может во всех случаях делать разрушение и копировать конструкцию, но это не тот контракт, который был выбран. Если перераспределение не требуется, то использование указанного оператора присваивания типов для таких вещей, как vector::operator=
и vector::assign
, может быть значительно более эффективным.
Ответ 2
Простой ответ: вы не можете. Если у вас есть переменные-члены const
, то компилятор не может предоставить оператор присваивания по умолчанию. Тем не менее, многие из операций, которые std::vector
обеспечивают, требуют выполнения назначений и, следовательно, требуют (публичного) оператора присваивания копий.
Ваши варианты:
- Сделайте
name
не const
.
- Напишите свой собственный оператор присваивания копий и подумайте о способе работы с "копированием" члена
const
.
Ответ 3
A vector
часто нужно перемещать элементы вокруг. Каждый раз, когда вектор должен расти, когда вы вызываете push_back()
, он перераспределяет память, чтобы поддерживать непрерывность, и копирует все существующие элементы в новое пространство. Также, если вы вызываете insert()
или remove()
, элементы должны
смещаться. Для vector
, чтобы иметь возможность делать все, что элементы должны быть назначены для копирования, а это означает, что тип, который вы храните в векторе, должен иметь определенный оператор присваивания.
В общем случае, если вы определяете класс, компилятор сгенерирует для вас оператор присваивания для этого класса. Однако есть случаи, когда компилятор не может этого сделать. Один из таких случаев - когда класс имеет постоянные члены (обратите внимание, что указатели на константу в порядке).
Итак, в вашем случае проблема заключается в const string name
. Он не позволяет компилятору генерировать operator=()
, что, в свою очередь, предотвращает компиляцию vector
, даже если вы фактически не используете назначение на своих элементах самостоятельно.
Одно из решений состоит в том, чтобы сделать name
неконстантным. Другой - написать свой собственный Student::operator=()
, каким-то образом это имеет смысл. Третий способ, как вы указали, - использовать вектор указателей, а не вектор объектов. Но тогда вы должны обрабатывать их распределение и выделение.
P.S. Другой случай, когда компилятор не может сгенерировать operator=
, - это когда ваш класс имеет члены, которые являются ссылками.
Ответ 4
Элементы векторов должны быть назначены для копирования, а ваша структура Student
не из-за члена const
. Просто используйте string name
вместо const string name
.
Если у вас нет конкретного требования, постоянные члены в классах редко бывают полезны. Если вы хотите предотвратить изменения в члене, сделайте его закрытым и добавьте открытую функцию getter.