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

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

#include 

int main() {
    std::vector  v;  
}

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

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

vector  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

60
задан Community 23 May 2017 в 12:09
поделиться

13 ответов

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

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

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

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

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

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

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

22
ответ дан 24 November 2019 в 17:53
поделиться

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

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

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

v [1] = 123;

]
0
ответ дан 24 November 2019 в 17:53
поделиться

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

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

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

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

1
ответ дан 24 November 2019 в 17:53
поделиться

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

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

1
ответ дан 24 November 2019 в 17:53
поделиться

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

#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];
}
2
ответ дан 24 November 2019 в 17:53
поделиться

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

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

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

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

1
ответ дан 24 November 2019 в 17:53
поделиться

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

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

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

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

.
4
ответ дан 24 November 2019 в 17:53
поделиться

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

7
ответ дан 24 November 2019 в 17:53
поделиться

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

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

1
ответ дан 24 November 2019 в 17:53
поделиться

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

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

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

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

РЕДАКТИРОВАТЬ : если вы хотите в основном оставить контейнер без изменений, но все же иметь возможность изменить его в определенных местах кода, затем используйте const ref в большинстве мест и затем предоставив код который должен иметь возможность изменить прямой доступ к контейнеру, или неконстантная ссылка сделает это возможным.

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

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

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

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

17
ответ дан 24 November 2019 в 17:53
поделиться

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

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

0
ответ дан 24 November 2019 в 17:53
поделиться

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

0
ответ дан 24 November 2019 в 17:53
поделиться

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

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

10
ответ дан 24 November 2019 в 17:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: