В чем разница между константным и неконстантным ключом?

В чем разница между двумя следующими строками?

map<int, float> map_data;
map<const int, float> map_data;

person NullPoiиteя    schedule 14.07.2013    source источник
comment
Вопрос знаком с stackoverflow.com/questions/6307321/   -  person soerium    schedule 14.07.2013


Ответы (7)


  • int и const int — это два разных типа.

  • std::map<int, float> и std::map<const int, float> также являются разными типами.

Разница между std::map<const int, float> и std::map<int, float> в какой-то степени аналогична разнице, скажем, между std::map<int, float> и std::map<std::string, float>; для каждого вы получаете новый тип карты.

В случае, отличном от const, тип внутреннего ключа является по-прежнему отличным от const int:

std::map<const int, float>::key_type       => const int
std::map<int, float>::key_type             => int

Однако ключи карты семантически неизменяемы, и все операции карты, которые разрешают прямой доступ к ключам (например, разыменование итераторов, что дает value_type), constисправляют key_type:

std::map<const int, float>::value_type => std::pair<const int, float>
std::map<int, float>::value_type       => std::pair<const int, float>

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

Однако это не всегда так: стандарт официально требует, чтобы ваш тип ключа был копируемым и перемещаемым, а некоторые реализации повторно используют узлы карты; в этих реализациях попытка использовать ключ const просто не сработает.

person Lightness Races in Orbit    schedule 14.07.2013
comment
So the difference is largely invisible to you in every way that matters. -- если вы не используете stdlib, который копирует/перемещает ключи (например, libc++), в этом случае версия const просто ломается. См. lists.cs.uiuc.edu/pipermail/cfe. -dev/2011-July/015926.html для соответствующего обсуждения. - person mitchnull; 13.10.2014
comment
@mitchnull Да, хорошее место. (кстати !) - person Lightness Races in Orbit; 13.10.2014
comment
@LightnessRacesinOrbit стандарт официально требует, чтобы ваш тип ключа был копируемым и перемещаемым. Что касается возможности перемещения, я не могу найти это в стандартной копии С++, которая у меня есть, не могли бы вы указать ссылку или номер раздела? - person beren; 04.10.2016
comment
Ах, спасибо, что подтвердили, что ключи не могут быть константами. Я хотел, чтобы мои ключи были неизменяемыми, и это сводило меня с ума -_- - person Noel Widmer; 30.04.2017
comment
@NoelWidmer: они уже будут неизменяемы для пользователей вашего типа карты, поэтому, даже если сделать ключи const нормально, они вам не понадобятся. - person Lightness Races in Orbit; 01.05.2017

ключ уже имеет значение const, поэтому в данном случае писать const излишне. После ввода элемента его key нельзя изменить.


Изменить:

Как упоминалось в комментариях, между двумя строками есть разница. Например, если вы пишете функцию, которая принимает map<const int, int>, вы не можете передать ей map<int, int>, так как они разные типы.

Но обратите внимание, что, хотя они и относятся к разным типам, они ведут себя одинаково, поскольку ключ на карте в любом случае является const...

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

person Maroun    schedule 14.07.2013
comment
Это не (полностью) правильно. Интерфейс std::map предоставляет тип ключа как const, но это не означает, что два экземпляра шаблона совпадают, как может подразумевать этот ответ. std::map<const int, float> и std::map<int, float> относятся к разным типам. - person jrok; 14.07.2013
comment
@jrok правильный, а этот ответ - нет. key_type на самом деле все еще int в первом случае. - person Lightness Races in Orbit; 14.07.2013
comment
Я редактирую свой ответ, надеюсь, теперь он лучше. Спасибо за ваши комментарии и исправления. - person Maroun; 14.07.2013
comment
@Maroun: Почти, но ключ не const (т.е. geordi << TYPE<std::map<int, float>::key_type>; # int). Однако семантически он неизменен, и все операции, которые напрямую обращаются к ключам (например, разыменование итератора), дают вам const версию значения ключа (т. е. geordi << TYPE<std::map<int, float>::value_type>; # std::pair<int const, float>). - person Lightness Races in Orbit; 14.07.2013
comment
@johnmac2332: Пусть это будет уроком, что быстро != идеально, а голосование != правильно. - person Lightness Races in Orbit; 14.07.2013
comment
@ johnmac2332 Быстрые и быстрые ответы должны быть высоко оценены. Это качество Stackoverflow лучше, чем у других веб-сайтов. ОП может дополнительно запросить в разделе комментариев. если он говорит спасибо, значит, вероятно, он получил ответ. - person Grijesh Chauhan; 14.07.2013
comment
Никто не идеален, все мы делаем ошибки и учимся друг у друга. Мы здесь, чтобы учиться и помогать :) - person Maroun; 15.07.2013
comment
@Grijesh: К сожалению, ОП может думать, что получил ответ, потому что он казался правдоподобным, но на самом деле был введен в заблуждение и продолжит распространять дезинформацию. Скорость/спешка абсолютно не заменяет правильность. Почему все так торопятся!? - person Lightness Races in Orbit; 15.07.2013
comment
@Maroun: Говори за себя - я здесь ради репутации и славы! :) - person Lightness Races in Orbit; 15.07.2013
comment
@LightnessRacesinOrbit Хорошо, да! Я с тобой согласен. Кстати, вы опубликовали хороший ответ. И да, нужно потратить время, чтобы опубликовать и принять ответ. Сам я большую часть времени стараюсь объяснять фундаментально и публиковать длинные ответы (но, конечно, с задержкой и с низким количеством голосов). в любом случае, я поздравляю Маруна с его 10 000 RP. Марун опубликовал много хороших ответов, и я нашел его ценным вкладчиком. - person Grijesh Chauhan; 15.07.2013
comment
@GrjeshChauhan: Да, аналогично. Эта фактическая ошибка не в характере! - person Lightness Races in Orbit; 15.07.2013

Отличие в том, что второй вариант установит тип ключа для карты как const int. С точки зрения «модифицируемости» это избыточно, так как карта уже хранит свои ключи как const объекты.

Однако это также может привести к неожиданным и неочевидным различиям в поведении этих двух карт. В C++ специализация шаблона, написанная для типа T, отличается от специализации, написанной для типа const T. Это означает, что две вышеупомянутые версии карты могут в конечном итоге использовать разные специализации различных «спутниковых» шаблонов, которые зависят от типа ключа. Одним из примеров является ключевой предикат компаратора. Первый будет использовать std::less<int>, а второй — std::less<const int>. Используя эту разницу, вы можете легко заставить эти карты сортировать элементы в другом порядке.

Подобные проблемы более очевидны с новыми контейнерами C++11, такими как std::unordered_map. std::unordered_map<const int, int> даже не будет компилироваться, так как попытается использовать std::hash<const int> специализацию для хеширования ключей. Такой специализации нет в стандартной библиотеке.

person AnT    schedule 14.07.2013

const нельзя изменить после установки. И да, согласно документам и другим ответам, вы должны помнить, что key уже const.

Ссылка: http://www.cplusplus.com/reference/map/map/ Ссылка: http://en.cppreference.com/w/cpp/container/map

person Shumail    schedule 14.07.2013
comment
Извините, я должен был написать - не могу. Моды внесли правку - Спасибо - person Shumail; 14.07.2013

Хотя поведение вашего приложения обычно будет таким же, оно имеет значение для некоторых компиляторов, которые вы можете использовать. Более конкретный пример того, что привело меня на эту страницу в первую очередь:

Явное указание карты как map<const key, value> успешно строится с помощью инструментария gnu;

Однако это приводит к сбою сборки Studio12 Solaris x86.


map<key, value> успешно строится на обоих. Поведение приложения не изменилось.

person blgt    schedule 15.08.2013
comment
Сбои каким образом? - person Lightness Races in Orbit; 06.10.2013
comment
@LightnessRacesinOrbit Он жаловался на то, что std::map::insert имеет несколько объявлений. - person blgt; 07.10.2013
comment
Да, как я сказал выше: это имеет значение для компилятора. - person blgt; 07.10.2013
comment
Обычно, когда мы говорим о сбое, мы имеем в виду неожиданное и неизящное завершение процесса во время выполнения. Сбои компилятора случаются редко, но случаются (особенно с новыми функциями языка) и очень серьезны по своей природе (по результатам сборки). - person Lightness Races in Orbit; 07.10.2013
comment
Это крашит мою сборку, а не приложение. Я неправильно использую терминологию? - person blgt; 07.10.2013
comment
По-моему, боюсь. :) Я бы сказал, что это сломает вашу сборку, но сбой кажется довольно специфичным, а не тем, что здесь происходит. YMMV, я полагаю. - person Lightness Races in Orbit; 07.10.2013

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

#include <map>
#include <string>

int glob = 10;

int main() {
    std::map<const int*, std::string> constKeyMap { { &glob, "foo"} };
    std::map<int*, std::string> keyMap { { &glob, "bar" } };

    for(const auto& kv : keyMap) { *(kv.first) = 20; }; // glob = 20
    for(const auto& kv : constKeyMap) { *(kv.first) = 20; }; // COMPILE ERROR

    return 0;
}
person SiimKallas    schedule 16.02.2014
comment
Когда key_type равно const int*, сам указатель не является константным, но указанный int является константным. - person lrineau; 02.04.2014

const относится к константе, которая после определения не может быть изменена, а затем... неконстантный ключ подвергается изменению... или даже не может быть изменен, просто в константе гарантируется "отсутствие изменений" (после определения) , а «изменение» может происходить или не происходить в неконстантных вещах.

person r0u9hn3ck    schedule 14.07.2013