Идея получения итератора для значений состоит в том, чтобы использовать его в алгоритмах STL, например, пересечение ключей двух карт. Решение, включающее Boost, не позволяет этого, потому что оно создаст итератор Boost. Наихудший ответ набирает наибольшее количество голосов!
Ответы:
70
Если вам действительно нужно скрыть значение, которое возвращает «настоящий» итератор (например, потому что вы хотите использовать свой итератор-ключ со стандартными алгоритмами, чтобы они работали с ключами, а не парами), то взгляните на Boost's transform_iterator .
[Совет: просматривая документацию по Boost для нового класса, сначала прочтите "примеры" в конце. Тогда у вас есть шанс выяснить, о чем, черт побери, все остальное говорит :-)]
карта является ассоциативным контейнером. Следовательно, итератор - это пара ключей val. ЕСЛИ вам нужны только ключи, вы можете игнорировать часть значения из пары.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end();++iter){Key k = iter->first;//ignore value//Value v = iter->second;}
РЕДАКТИРОВАТЬ:: Если вы хотите выставить снаружи только ключи, вы можете преобразовать карту в вектор или ключи и выставить.
Но тогда было бы действительно плохой идеей выставлять итератор вектора снаружи.
Naveen
Не выставляйте итератор. Просто укажите ключи в векторе
aJ.
5
Вы можете сделать это вместо этого: const Key& k(iter->first);
strickli
17
Две вещи, это отвечает на вопрос OP с точно ответом , который он уже знал и не искал, во- вторых , этот метод не поможет вам , если вы хотите сделать что - то вроде: std::vector<Key> v(myMap.begin(), myMap.end()).
Андреас Магнуссон
Не конвертируйте ключи в вектор. Создание нового вектора противоречит цели итерации, которая должна быть быстрой и ничего не выделять. Кроме того, это будет медленным для больших сетов.
Кевин Чен
85
В C ++ 11 итерационный синтаксис прост. Вы по-прежнему перебираете пары, но легко получить доступ только к ключу.
К сожалению, стандарт C ++ 17 требует, чтобы вы объявили valueпеременную, даже если вы ее не используете ( std::ignoreпоскольку можно было бы использовать for std::tie(..), не работает, см. Это обсуждение ).
Поэтому некоторые компиляторы могут предупреждать вас о неиспользуемой valueпеременной! Предупреждения во время компиляции относительно неиспользуемых переменных, на мой взгляд, недопустимы для любого производственного кода. Таким образом, это может быть неприменимо для определенных версий компилятора.
не могли бы вы назначить его на std :: ignore в принципе? Действительно ли это повредит эффективности скомпилированного кода или ничего не даст? (Я имею в виду не привязку, а скорее действие внутри цикла)
KotoroShinoto
Начиная с C ++ 17 вы также можете использовать [[might_unused]]. Это подавляет предупреждение. Как это:for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
arhuaco
15
Ниже более общее шаблонное решение, на которое ссылается Ян ...
Когда явное значение beginи endне требуется, например, для цикла по диапазонам, цикл по ключам (первый пример) или значениям (второй пример) может быть получен с помощью
#include<boost/range/adaptors.hpp>
map<Key,Value> m;for(auto k : boost::adaptors::keys(m))
cout << k << endl;for(auto v : boost::adaptors::values(m))
cout << v << endl;
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();for(; iter != endIter;++iter){
type key = iter->first;.....}
Да, я знаю, проблема в том, что у меня класс A {public: // я хотел бы выставить итератор по ключам приватной карты здесь private: map <>};
Богдан Балан
В этом случае, я думаю, вы можете создать std :: list, используя std :: trasnform и собирая только ключи с карты. Затем вы можете открыть итератор списка, так как добавление дополнительных элементов в список не приведет к аннулированию существующих итераторов.
Naveen
3
Если вам нужен итератор, который просто возвращает ключи, вам нужно обернуть итератор карты в ваш собственный класс, который предоставляет желаемый интерфейс. Вы можете объявить новый класс итератора с нуля, как здесь , или использовать существующие вспомогательные конструкции. Этот ответ показывает, как использовать Boost, transform_iteratorчтобы обернуть итератор в тот, который возвращает только значения / ключи.
Без Boost вы могли бы сделать это так. Было бы неплохо, если бы вы могли написать оператор приведения вместо getKeyIterator (), но я не могу заставить его скомпилировать.
Я знаю, что это не отвечает на ваш вопрос, но один из вариантов, который вы, возможно, захотите рассмотреть, - это просто наличие двух векторов с одним и тем же индексом, являющихся "связанной" информацией ...
если вы хотите подсчитать количество имен по имени, вы просто быстро выполните цикл для vName.size (), и когда вы найдете его, это индекс для vNameCount, который вы ищете.
Конечно, это может не дать вам всей функциональности карты, и в зависимости от этого может быть или не быть лучше, но это может быть проще, если вы не знаете ключей и не должны добавлять слишком много обработки.
Просто помните, когда вы добавляете / удаляете из одного, вы должны делать это из другого, иначе все станет сумасшедшим, хех: P
Ответы:
Если вам действительно нужно скрыть значение, которое возвращает «настоящий» итератор (например, потому что вы хотите использовать свой итератор-ключ со стандартными алгоритмами, чтобы они работали с ключами, а не парами), то взгляните на Boost's transform_iterator .
[Совет: просматривая документацию по Boost для нового класса, сначала прочтите "примеры" в конце. Тогда у вас есть шанс выяснить, о чем, черт побери, все остальное говорит :-)]
источник
карта является ассоциативным контейнером. Следовательно, итератор - это пара ключей val. ЕСЛИ вам нужны только ключи, вы можете игнорировать часть значения из пары.
РЕДАКТИРОВАТЬ:: Если вы хотите выставить снаружи только ключи, вы можете преобразовать карту в вектор или ключи и выставить.
источник
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.В C ++ 11 итерационный синтаксис прост. Вы по-прежнему перебираете пары, но легко получить доступ только к ключу.
источник
Без повышения
Вы можете сделать это, просто расширив итератор STL для этой карты. Например, отображение строк в целые числа:
Вы также можете выполнить это расширение в шаблоне для более общего решения.
Вы используете свой итератор точно так же, как и итератор списка, за исключением того, что вы выполняете итерацию по карте
begin()
иend()
.источник
template<typename C> class key_iterator : public C::iterator
и т. д.В C ++ 17 вы можете использовать структурированную привязку внутри цикла for на основе диапазона (соответственно адаптируя ответ Джона Х. ):
К сожалению, стандарт C ++ 17 требует, чтобы вы объявили
value
переменную, даже если вы ее не используете (std::ignore
поскольку можно было бы использовать forstd::tie(..)
, не работает, см. Это обсуждение ).Поэтому некоторые компиляторы могут предупреждать вас о неиспользуемой
value
переменной! Предупреждения во время компиляции относительно неиспользуемых переменных, на мой взгляд, недопустимы для любого производственного кода. Таким образом, это может быть неприменимо для определенных версий компилятора.источник
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Ниже более общее шаблонное решение, на которое ссылается Ян ...
Все кредиты принадлежат Яну ... Спасибо, Ян.
источник
Вы ищете map_keys , с его помощью вы можете писать такие вещи, как
источник
BOOST_FOREACH(const key_t& key, ...
Вот пример того, как это сделать с помощью transform_iterator Boost
источник
Когда явное значение
begin
иend
не требуется, например, для цикла по диапазонам, цикл по ключам (первый пример) или значениям (второй пример) может быть получен с помощьюисточник
Вы хотите это сделать?
источник
Если вам нужен итератор, который просто возвращает ключи, вам нужно обернуть итератор карты в ваш собственный класс, который предоставляет желаемый интерфейс. Вы можете объявить новый класс итератора с нуля, как здесь , или использовать существующие вспомогательные конструкции. Этот ответ показывает, как использовать Boost,
transform_iterator
чтобы обернуть итератор в тот, который возвращает только значения / ключи.источник
Ты мог
std::map<K,V>::iterator
std::transform
в вашей ,map.begin()
чтобыmap.end()
сboost::bind( &pair::second, _1 )
функтором->second
член во время итерации сfor
циклом.источник
Этот ответ похож на ответ Родригоба, только без
BOOST_FOREACH
. Вместо этого вы можете использовать диапазон С ++.источник
Без Boost вы могли бы сделать это так. Было бы неплохо, если бы вы могли написать оператор приведения вместо getKeyIterator (), но я не могу заставить его скомпилировать.
источник
Для потомков, и поскольку я пытался найти способ создать диапазон, альтернативой является использование boost :: adapters :: transform
Вот небольшой пример:
Если вы хотите перебрать значения, используйте
t.second
лямбда.источник
Здесь много хороших ответов, ниже представлен подход, использующий пару из них, который позволяет вам написать это:
Если вы всегда этого хотели, то вот код для MapKeys ():
источник
Я принял ответ Яна для работы со всеми типами карт и исправил возврат ссылки для
operator*
источник
Я знаю, что это не отвечает на ваш вопрос, но один из вариантов, который вы, возможно, захотите рассмотреть, - это просто наличие двух векторов с одним и тем же индексом, являющихся "связанной" информацией ...
Так что в ..
если вы хотите подсчитать количество имен по имени, вы просто быстро выполните цикл для vName.size (), и когда вы найдете его, это индекс для vNameCount, который вы ищете.
Конечно, это может не дать вам всей функциональности карты, и в зависимости от этого может быть или не быть лучше, но это может быть проще, если вы не знаете ключей и не должны добавлять слишком много обработки.
Просто помните, когда вы добавляете / удаляете из одного, вы должны делать это из другого, иначе все станет сумасшедшим, хех: P
источник