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


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

#include <vector>

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

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

этой трудности, задавать вопросы). Мой реальный базовый вариант использования-это что-то вроде этого:
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
14 60

14 ответов:

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

в C++03 есть пункт 23.1[lib.стеклотара.требования] / 3, в котором говорится

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

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

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

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

вы все равно не сможете отсортировать вектор в месте, хотя.

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

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

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

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

EDIT: если то, что вы хотите сделать, - это в основном оставить контейнер неизменным, но все же иметь возможность изменить его в определенных местах в коде, то использование const ref в большинстве мест, а затем предоставление кода, который должен иметь возможность изменить прямой доступ к контейнеру или неконстантный ref, сделает это вероятный.

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

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

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

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

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

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

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

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

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

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

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

У меня были бы все мои объекты 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];
}

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

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

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

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

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

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

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> синтаксис, даже если он работал.

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

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

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

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

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

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

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

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

v[1] = 123;

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

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

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

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

underlying_array[size()] = passed_value;

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

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