Почему std :: vector передает свою константу содержащимся объектам? - PullRequest
4 голосов
/ 17 августа 2011

A const int * и int *const очень разные.Аналогично с const std::auto_ptr<int> против std::auto_ptr<const int>.Тем не менее, похоже, нет такого различия с const std::vector<int> против std::vector<const int> (на самом деле я не уверен, что второе даже разрешено).Почему это так?

Иногда у меня есть функция, которой я хочу передать ссылку на вектор.Функция не должна изменять сам вектор (например, no push_back()), но она хочет изменить каждое из содержащихся значений (скажем, увеличить их).Точно так же я мог бы хотеть, чтобы функция только изменяла векторную структуру, но не изменяла любое из ее существующего содержания (хотя это было бы странно).Подобные вещи возможны с std::auto_ptr (например), но поскольку std::vector::front() (например) определяется как

const T &front() const;
T &front();

, а не просто

T &front() const;

Там нетспособ выразить это.

Примеры того, что я хочу сделать:

//create a (non-modifiable) auto_ptr containing a (modifiable) int
const std::auto_ptr<int> a(new int(3));

//this works and makes sense - changing the value pointed to, not the pointer itself
*a = 4;
//this is an error, as it should be
a.reset();


//create a (non-modifiable) vector containing a (modifiable) int
const std::vector<int> v(1, 3);

//this makes sense to me but doesn't work - trying to change the value in the vector, not the vector itself
v.front() = 4;
//this is an error, as it should be
v.clear();

Ответы [ 4 ]

7 голосов
/ 17 августа 2011

Это дизайнерское решение.

Если у вас есть const контейнер, обычно понятно, что вы не хотите, чтобы кто-либо изменял содержащиеся в нем элементы, которые являются его неотъемлемой частью. То, что контейнер полностью "владеет" этими элементами, "укрепляет связь", если хотите.

Это в отличие от исторических, более низкоуровневых реализаций «контейнеров» (то есть необработанных массивов), которые более практичны. Как вы совершенно правильно сказали, между int const* и int * const есть большая разница. Но стандартные контейнеры просто выбирают для передачи const ness.

4 голосов
/ 17 августа 2011

Разница в том, что указатели на int не владеют целыми числами, на которые они указывают, тогда как vector<int> владеет содержащимися целыми числами.vector<int> может быть концептуализирован как структура с элементами int, где число элементов просто является переменным.

Если вы хотите создать функцию, которая может изменять значения, содержащиеся в векторе, но неСам вектор, тогда вы должны спроектировать функцию, принимающую аргументы итератора.

Пример:

void setAllToOne(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
    std::for_each(begin, end, [](int& elem) { elem = 1; });
}

Если вы можете позволить себе поместить желаемую функциональность в заголовок, тогда она может быть сделана общей как:

template<typename OutputIterator>
void setAllToOne(OutputIterator begin, OutputIterator end)
{
    typedef typename iterator_traits<OutputIterator>::reference ref;
    std::for_each(begin, end, [](ref elem) { elem = 1; });
}
1 голос
/ 17 августа 2011
  • Вы правы, невозможно иметь вектор const int в первую очередь потому, что элементы не могут быть назначены (требования к типу элемента, содержащегося в векторе).
  • Если вывам нужна функция, которая только изменяет элементы вектора, но не добавляет элементы к самому вектору, это в первую очередь то, что STL делает для вас - иметь функции, которые не зависят от того, в каком контейнере содержится последовательность элементов. Функция просто беретпара итераторов и делает свое дело для этой последовательности, совершенно не обращая внимания на тот факт, что они содержатся в векторе.
  • Посмотрите "вставки итераторов", чтобы узнать о том, как вставить что-то в контейнер безнужно знать, что это за элементы.Например, back_inserter принимает контейнер, и все, что ему нужно, это знать, что контейнер имеет функцию-член, называемую «push_back».
1 голос
/ 17 августа 2011

Синтаксически одна большая проблема с тем, что вы предлагаете, заключается в следующем: std::vector<const T> не тот же тип, что и std::vector<T>.Следовательно, вы не можете передать vector<T> в функцию, которая ожидает vector<const T> без какого-либо преобразования.Не простой актерский состав, а создание нового vector<const T>.И этот новый не мог просто делиться данными со старыми;ему нужно будет либо скопировать, либо переместить данные из старого в новый.

С этим можно обойтись с помощью std::shared_ptr, но это потому, что это общие указатели.У вас может быть два объекта, которые ссылаются на один и тот же указатель, поэтому преобразование из std::shared_ptr<T> в shared_ptr<const T> не повредит (за исключением увеличения счетчика ссылок).Не существует такой вещи, как shared_vector.

std::unique_ptr, которая тоже работает, потому что их можно только перемещать, а не копировать.Следовательно, только один из них когда-либо будет иметь указатель.

Так что то, о чем вы просите, просто невозможно.

...