В прошлый раз мы рассмотрели вектор ссылок. А давайте чуть углубимся сюда и посмотрим, как будет себя вести вектор констант.
Константные объекты уже удовлетворяют требованию Erasable. Для них либо определен деструктор(пользовательские объекты), либо это константные тривиальные типы, которые тоже Erasable.
Казалось бы на этом можно закончить пост, можно создавать и ладно, много бубнить не нужно об этом. Но вот при использовании этой сущности могут возникать интересные эффекты.
struct A {
A(int num) : a{num} {}
int a;
};
std::vector<const A> vec;
for (int i = 0; i < 5; i++) {
vec.push_back(i);
}
for (int i = 0; i < 5; i++) {
std::cout << vec[i].a << std::endl;
}Ничего сверхъестественного. Просто создаем вектор, заполняем его и выводим. Что может пойти не так?
Это дело не соберется на методе push_back. Тип А - тривиально копируемый, что не допускается при вызове этого метода. Но как только мы добавим нетривиальный деструктор или конструктор копирования - все заработает нормально. Этот же факт значит, что для любых тривиальных типов вы не сможете добавлять так элементы в вектор констант. Не знаю, какие рассуждения лежат за этим, знающие могут оставить свои мысли в комментах.
Но это ладно. Дальше мы хотим поработать с этим вектором и, например, отсортировать его.
struct A {
A(int num) : a{num} {}
~A() {} // Important here
int a;
};
std::vector<const A> vec;
for (int i = 0; i < 5; i++) {
vec.push_back(i);
}
std::sort(vec.begin(), vec.end())И тоже натыкаемся на ошибку компиляции. Внутри себя std::sort использует std::swap, которая меняет значения своих операндов inplace. Это значит, что мы должны иметь возможность присваивать объектам другие данные. А для константных объектов сделать это будет очень проблематично.
Метод erase также нерабочий из-за отсутствия возможности присваивания. erase позволяет удалять элементы из середины вектора. Для этого придется "сдвигать" все элементы справа от удаляемых, чтобы заполнить пустоту. Делается это либо перемещением, либо копированием. Но для константных объектов очевидно запрещено вызывать оператор присваивания.
И хотя, интерфейс такого контейнера будет ограничен, мы все равно можем использовать читающие алгоритмы над ним. Например, подсчитывать какую-нибудь статистику по элементам. Также мы можем в рантайме свободно добавлять и удалять элементы из контейнера. Через emplace_back и pop_back. И это выгодно выделяет вектор констант на фоне константного вектора. Вы не хотите изменять сами элементы, но хотите иметь возможность изменять их множество и выполнять различные читающие операции над ним. Именно для этих задач и подходит вектор констант. Главное - аккуратнее с интерфейсом)
УПД: В комментах указали на критические неточности в посте, завтра будет опровержение
Be careful. Stay cool.