Выдает ли map :: iterator lvalues? - PullRequest
       22

Выдает ли map :: iterator lvalues?

3 голосов
/ 21 сентября 2010

Другими словами, когда i является map<K,V>::iterator, обеспечивают ли следующую ожидаемую семантику (т. Е. Она изменяет карту):

*i = make_pair(k, v);
i->first = k;
i->second = v;

?

Обновление: Первые две строки недопустимы, так как возвращаемое значение operator* (конвертируется в?) A pair<const K, V>.Как насчет третьей строки?

Предполагая да ответ на три вопроса, это будет означать, что:

  • Либо map<K,V> элементы сохраняются как pair<K,V> где-то,
  • Или есть какой-то умный прокси-класс, который возвращает map<K,V>::iterator::operator*.В этом случае, как реализуется operator->?

Ответы [ 4 ]

4 голосов
/ 21 сентября 2010

Я пытался отследить это через стандарт:

  • Для map<Key,T> value_type равно pair<const Key,T> для 23.3.1 / 2

  • Класс карты поддерживает двунаправленные итераторы, согласно 23.3.1 / 1

  • Двунаправленный итератор удовлетворяет требованиям для прямых итераторов, согласно 24.1.4 / 1

  • Для прямого итератора a с value_type T выражение *a возвращает T & (не тип "преобразуемый в T", как некоторые другие итераторы делают) (Таблица 74 в 24.1.3)

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

3 голосов
/ 21 сентября 2010

карта почти как набор пар. Да, его итератор, вероятно, реализован как указатель на узел с pair<const K,V>. Однако ваш код недействителен, потому что значения на самом деле имеют тип pair<const K, V>, поэтому вы не можете присвоить first.

* я возвращаю pair<const K, V>& или какой-либо прокси, который ведет себя как этот тип (не удается найти резервную копию последнего утверждения в стандарте). Вы можете реализовать такие прокси, перегрузив operator ->.

1 голос
/ 21 сентября 2010
map<K,V>::iterator i = my_map.begin();

*i = make_pair(k, v); // invalid, you cannot assign to a pair<const K,V>&
i->first = k; // invalid cannot write to const
i->second = v; // valid
1 голос
/ 21 сентября 2010

Во-первых, технически унарный оператор * в этом случае оценивается как lvalue.Однако термин lvalue в C в основном обозначает то, что имеет местоположение (адрес) в хранилище (памяти).В терминологии C ++ четными функциями являются lvalues ​​.Итак, опять же, в вашем примере выше унарный * дает lvalue.Вы можете взять адрес этого lvalue, если вы хотите это сделать, т.е. вы можете оценить &*i, &i->first и &i->second (при условии, что встроенный унарный &).

Во-вторых, поскольку ваш исходный пример включает в себя присваивание, вы должны говорить о изменяемых значениях .Видите ли, свойство быть lvalue само по себе очень мало связано с назначаемостью.Чтобы использовать встроенный оператор присваивания, вам нужно изменить значение lvalue .То, что вы получаете от разыменованного итератора, это value_type из std::map.Как вы уже знаете, это пара с квалифицированным первым членом.Это автоматически делает первый член неизменяемым, и это делает всю пару неизменяемой встроенным оператором присваивания.Второй член пары является изменяемым, как вы уже заметили.

Итак, еще раз, оператор разыменования в этом случае возвращает lvalue.Это lvalue не модифицируемо в целом, и его первый член также не модифицируем.Его второй член - модифицируемое lvalue.

Что касается вашего предположения о том, как хранятся элементы std::map, я бы сказал, что в типичной реализации они будут храниться как pair<const K, V> объекты, то есть именно то, чтооператор разыменования оценивается как.Обычно карте не нужно изменять ключевую часть пары после ее инициализации, поэтому не должно возникать никаких проблем с тем фактом, что первый член пары является константно-квалифицированным.

...