2016-08-04 2 views
26

Рассмотрим простую программу ниже, которая пытается перебирать значений набора с помощью неконстантная ссылки на элементы в нем:Почему std :: set кажется принудительным для использования const_iterator?

#include <set> 
#include <iostream> 

class Int 
{ 
public: 
    Int(int value) : value_(value) {} 
    int value() const { return value_; } 
    bool operator<(const Int& other) const { return value_ < other.value(); } 
private: 
    int value_; 
}; 

int 
main(int argc, char** argv) { 
    std::set<Int> ints; 
    ints.insert(10); 
    for (Int& i : ints) { 
     std::cout << i.value() << std::endl; 
    } 
    return 0; 
} 

При компиляции, я получаю сообщение об ошибке с GCC:

test.c: In function ‘int main(int, char**)’: 
test.c:18:18: error: invalid initialization of reference of type ‘Int&’ from expression of type ‘const Int’ 
for (Int& i : ints) { 
      ^ 

Да, я знаю, что на самом деле я не пытаюсь модифицировать элементы в цикле for. Но дело в том, что я должен иметь возможность использовать неконстантную ссылку для использования внутри цикла, поскольку сам набор не является const. Я получаю ту же ошибку, если создаю функцию setter и использую ее в цикле.

+0

Я объяснил это подробно, здесь: [Ошибка: прохождение ххх, как «это» аргумент ххх выбросе классификаторов] (http://stackoverflow.com/questions/ 5973427/error-through-xxx-as-this-of-xxx-discards-qualifiers) – Nawaz

+0

Если вы действительно хотите изменить элемент 'std :: set', вы можете использовать' const_cast'. Просто убедитесь, что эта модификация не изменяет порядок элемента в наборе, или вы столкнетесь с неопределенным поведением. Это очень небезопасно, поэтому вы должны идти _way_, чтобы сделать это. – gnzlbg

ответ

35

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

+1

Я вижу это, но я могу что-то изменять в элементе set, который не изменяет порядок сортировки в наборе; [http://www.cplusplus.com/reference/set/set/begin](http://www.cplusplus.com/reference/set/set/begin) утверждает, что неконстантный набор вернет не- const iterator from begin() – atomicpirate

+5

std :: set не может знать, что он делает все 'const' безопасным. Если ваша структура имеет изменяемые данные и некоторые неизменные ключевые поля, то, возможно, вам следует использовать карту со структурой со значением в качестве значения и копию неизменяемых ключевых полей в качестве ключа. Вы всегда можете const_cast покинуть const, если знаете, что вы не меняете порядок значений, но я думаю, что подход карты более чист. – nate

+5

Согласно http://en.cppreference.com/w/cpp/container/set, в то время как 'begin' возвращает' iterator', что итератор является псевдонимом для 'const_iterator', поскольку C++ 11 – nate

4

Поведение по дизайну.

Предоставление вам неконстантного итератора может вдохновить вас на изменение элемента в наборе; последующее итерационное поведение тогда будет неопределенным.

Обратите внимание, что стандарт C++ говорит, что set<T>::iterator - это const, поэтому старомодный метод pre C++ 11 все равно не будет работать.

8

std::set использует приведенные значения для формирования быстрой структуры данных (обычно это красно-черное дерево). Изменение значения означает, что вся структура должна быть изменена. Таким образом, форсирование const ness, std::set не позволяет вам перевести его в нерабочее состояние.

+0

«не используется», как в случае «случайных сбоев при попытке его использования». – Yakk

+2

@Yakk, скорее всего, просто не удается найти элементы на некоторых операциях, что, возможно, хуже. – OrangeDog

7

От cpp reference:

In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.