Могу ли я использовать константу в векторах, чтобы добавлять элементы, но не изменять уже добавленные?

Мои комментарии к этому ответу заставили меня задуматься о проблемах состроения и сортировки. Я немного поиграл и уменьшил свои проблемы до того, что этот код:

#include <vector>

int main() {
    std::vector <const int> v;  
}

не будет компилироваться - вы не можете создать вектор const int. Очевидно, я должен был знать это (и интеллектуально я это сделал), но мне никогда не приходилось создавать такую ​​вещь раньше. Однако мне кажется, что это полезная конструкция, и я задаюсь вопросом, существует ли какой-либо путь вокруг этой проблемы - Я хочу добавить вещи к вектору (или что-то еще), но они не должны меняться после добавления.

Наверное, это довольно сложное решение, но я никогда не думал об этом раньше. Вероятно, я не должен был упоминать сортировку (я могу задать еще один вопрос об этом, см. этот в связи с трудностями задавать вопросы). Мой реальный случай использования базы - это примерно так:

vector <const int> v;     // ok (i.e. I want it to be OK)
v.push_back( 42 );        // ok
int n = v[0];             // ok
v[0] = 1;                 // not allowed

Ответ 1

Ну, в С++ 0x вы можете...

В С++ 03 есть параграф 23.1 [lib.containers.requirements]/3, в котором говорится, что

Тип объектов, хранящихся в этих компонентах, должен удовлетворять требованиям типов CopyConstructible (20.1.3) и дополнительным требованиям типов Assignable.

Это то, что в настоящее время не позволяет использовать const int как аргумент типа std::vector.

Однако в С++ 0x этот абзац отсутствует, вместо этого T требуется Destructible и дополнительно требования к T указаны для каждого выражения, например v = u on std::vector действует только в том случае, если T MoveConstructible и MoveAssignable.

Если я правильно интерпретирую эти требования, должно быть возможно создать экземпляр std::vector<const int>, вам просто не хватает некоторых его функциональных возможностей (что, я думаю, это то, что вы хотели). Вы можете заполнить его, передав пару итераторов конструктору. Я думаю, что emplace_back() должен работать, хотя я не нашел явных требований для T для него.

Вы все равно не сможете сортировать вектор на месте.

Ответ 2

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

Ближайшее решение, которое, я полагаю, возможно, будет состоять в использовании какой-либо косвенности. Таким образом, у вас может быть указатель на const или у вас может быть объект, который содержит требуемое значение, но значение не может быть изменено внутри объекта (например, вы получили бы с Integer в Java).

Наличие элемента в определенном индексе неизменным противоречит тому, как работают стандартные контейнеры. Возможно, вы сможете создать свои собственные, которые работают таким образом, но стандартные - нет. И ни один из них, основанный на массивах, не будет работать независимо от того, если вы не сможете установить их инициализацию в синтаксис инициализации {a, b, c}, поскольку после создания массива из const вы не сможете его изменить. Таким образом, класс vector вряд ли будет работать с элементами const независимо от того, что вы делаете.

Наличие const в контейнере без какой-либо косвенности просто не очень хорошо работает. Вы в основном просите сделать весь контейнер const - который вы можете сделать, если вы скопируете его из уже инициализированного контейнера, но вы действительно не можете иметь контейнер - конечно, не стандартный контейнер - который содержит константы без каких-либо вид косвенности.

ИЗМЕНИТЬ. Если вы хотите, чтобы в основном оставить контейнер неизменным, но все же можно изменить его в определенных местах в коде, а затем использовать const ref в большинстве мест и то предоставление кода, который должен иметь возможность изменить прямой доступ к контейнеру, или не-const ref, сделает это возможным.

Итак, используйте const vector<int>& в большинстве мест, а затем либо vector<int>&, где вам нужно изменить контейнер, либо предоставить часть кода для прямого доступа к контейнеру. Таким образом, это в основном неизменное, но вы можете изменить его, когда захотите.

С другой стороны, если вы хотите иметь возможность в значительной степени всегда изменять то, что в контейнере, но не изменять определенные элементы, то я бы предложил разместить класс оболочки вокруг контейнера. В случае vector, оберните его и заставьте оператор индекса возвращать константу ref вместо not-const ref - либо это, либо копию. Итак, предположив, что вы создали templatized версию, ваш оператор индекса будет выглядеть примерно так:

const T& operator[](size_t i) const
{
    return _container[i];
}

Таким образом, вы можете обновить сам контейнер, но вы не можете изменить его отдельные элементы. И пока вы объявляете все функции inline, это не должно быть большим успехом (если вообще есть), чтобы иметь обертку.

Ответ 3

Вы не можете создать вектор const ints, и это было бы бесполезно, даже если бы вы могли. Если я удаляю второй int, тогда все оттуда сдвигается вниз на один - читается: modified - делает невозможным гарантировать, что v [5] имеет одно и то же значение в двух разных случаях.

Добавьте к этому, const не может быть назначен после того, как он был объявлен, за исключением того, что он отбрасывает константу. И если вы хотите сделать это, почему вы используете const в первую очередь?

Ответ 4

Вам нужно будет написать свой собственный класс. Вы можете использовать std::vector как свою внутреннюю реализацию. Затем просто реализуйте интерфейс const и те немногие неконстантные функции, которые вам нужны.

Ответ 5

Хотя это не соответствует всем вашим требованиям (возможность сортировки), попробуйте постоянный вектор:

int values[] = {1, 3, 5, 2, 4, 6};
const std::vector<int> IDs(values, values + sizeof(values));

Хотя вы можете использовать std::list. Со списком значения не нужно изменять, только ссылки на них. Сортировка выполняется путем изменения порядка ссылок.

Вам, возможно, придется расходовать силы мозга и писать свои собственные.: - (

Ответ 6

У меня были бы все мои объекты const в стандартном массиве.
Затем используйте вектор указателей в массив.
Небольшой класс полезности, чтобы помочь вам не удалять ссылки на объекты и сено-престо.

#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>


class XPointer
{
    public:
        XPointer(int const& data)
            : m_data(&data)
        {}

    operator int const&() const
    {
        return *m_data;
    }

    private:
        int const*  m_data;

};

int const               data[]    =  { 15, 17, 22, 100, 3, 4};

std::vector<XPointer>   sorted(data,data+6);


int main()
{
    std::sort(sorted.begin(), sorted.end());
    std::copy(sorted.begin(), sorted.end(), std::ostream_iterator<int>(std::cout, ", "));
    int x   = sorted[1];
}

Ответ 7

Я с Ноем: оберните вектор классом, который предоставляет только то, что вы хотите разрешить.

Если вам не нужно динамически добавлять объекты в вектор, рассмотрите std::tr1::array.

Ответ 8

Верно, что Assignable является одним из стандартных требований к типу векторного элемента, а const int не присваивается. Однако я ожидаю, что в продуманной реализации компиляция завершится неудачей, только если код явно использует назначение. Для std::vector, например, insert и erase.

В действительности во многих реализациях компиляция не выполняется, даже если вы не используете эти методы. Например, Comeau не может скомпилировать plain std::vector<const int> a;, потому что соответствующая специализация std::allocator не скомпилируется. Он не сообщает о ближайших проблемах с самим std::vector.

Я считаю, что это действительная проблема. Предполагается, что реализованная библиотекой реализация std::allocator завершится неудачей, если параметр типа является const-квалифицированным. (Интересно, можно ли создать пользовательскую реализацию std::allocator, чтобы заставить все это скомпилировать.) (Также было бы интересно узнать, как VS удается скомпилировать его). Снова, с Comeau std::vector<const int> не удается скомпилировать по тем же причинам std::allocator<const int> не скомпилируется, и, согласно спецификации std::allocator, он не должен компилироваться.

Конечно, в любом случае любая реализация имеет право не скомпилировать std::vector<const int>, так как она может выйти из строя по спецификации языка.

Ответ 9

Если в этом случае вам важна константа, я думаю, вы, вероятно, захотите работать с неизменяемыми типами. Концептуально вы будете иметь фиксированный размер, const-массив из const int s. Каждый раз, когда вам нужно его менять (например, для добавления или удаления элементов или для сортировки), вам нужно будет сделать копию массива с выполненной операцией и использовать это вместо этого. Хотя это очень естественно на функциональном языке, на С++ оно не кажется вполне "правильным". например, получение эффективных реализаций может быть сложным, но вы не говорите, каковы ваши требования к производительности. Если вы рассматриваете этот маршрут как заслуживающий внимания с точки зрения производительности/пользовательского кода или нет, я считаю, что это правильный подход.

После этого, конечно, удержание значений указателем-не указателем-указателем/умным указателем является лучшим (но имеет свои собственные накладные расходы, конечно).

Ответ 10

Я немного подумывал об этой проблеме, и кажется, что ваше требование отключено.

Вы не хотите добавлять неизменяемые значения в свой вектор:

std::vector<const int> vec = /**/;
std::vector<const int>::const_iterator first = vec.begin();

std::sort(vec.begin(), vec.end());

assert(*vec.begin() == *first); // false, even though `const int`

То, что вы действительно хотите, это ваш вектор для хранения постоянного набора значений в модифицируемом порядке, который не может быть выражен синтаксисом std::vector<const int>, даже если он сработал.

Я боюсь, что это чрезвычайно заданная задача, для которой потребуется выделенный класс.

Ответ 11

Используя только неспециализированный vector, это невозможно. Сортировка выполняется с помощью назначения. Итак, тот же код, который делает это возможным:

sort(v.begin(), v.end());

... также делает это возможным:

v[1] = 123;

Ответ 12

Вы можете получить класс const_vector из std::vector, который перегружает любой метод, возвращающий ссылку, и вместо этого возвращает значение ссылки const. Чтобы сделать свой вид, опустите вниз до std::vector.

Ответ 13

std::vector постоянного объекта, вероятно, не скомпилируется из-за требования Assignable, поскольку постоянный объект не может быть назначен. То же самое верно и для Move Assignment. Это также проблема, с которой я часто сталкиваюсь при работе с векторной картой, например boost flat_map или Loki AssocVector. Поскольку он имеет внутреннюю реализацию std::vector<std::pair<const Key,Value> >. Таким образом, почти невозможно выполнить требование константы map, которое может быть легко реализовано для любой карты на основе node.

Однако можно посмотреть, означает ли std::vector<const T>, что вектор должен хранить типизированный объект const T, или просто нужно возвращать не изменяемый интерфейс при доступе. В этом случае возможна реализация std::vector<const T>, которая следует за назначением Assignable/Move Assignable, поскольку она хранит объект типа T, а не const T. Стандартные типы typedefs и allocator мало нуждаются в поддержке стандартных требований. Однако для поддержки таких vector_map или flat_map, возможно, потребуется значительное изменение в интерфейсе std::pair, поскольку оно предоставляет непосредственные и непосредственные переменные-члены.

Ответ 14

Ошибка компиляции, потому что push_back() (например) в основном

underlying_array[size()] = passed_value;

где оба операнда T&. Если T - const X, который не может работать.

Составляющие элементы кажутся правильными в принципе, но на практике это неестественно, и спецификации не говорят, что они должны поддерживаться, поэтому их нет. По крайней мере, не в stdlib (потому что тогда это будет в векторе).