#c
Вопрос:
Дан класс Ac
и карта:
std::map<std::string, Ac> ma;
Какая из следующих форм лучше:
ma["112"] = Ac(); // (1)
или
Ac ac;
ma["112"] = ac; // (2)
Есть ли какие-либо проблемы с использованием версии 1?
Фактический код в контексте:
struct Ac
{
bool first;
bool second;
};
int main()
{
std::map<std::string, Ac> ma;
std::string st = "10.12";
if (ma.find(st) == ma.end())
{
#if 0
// 1
ma["112"] = Ac();
#else
// 2
Ac ac;
ma["112"] = ac;
#endif
}
else
ma[st].first = true;
return 0;
}
Комментарии:
1. Они не выражают одно и то же. в #2
ac
все еще существует и может использоваться после установки его копии на карте.2. это зависит от того, что вам нужно и что вы хотите сделать. Вы также можете вставить элемент через
ma["112"];
. Рассмотритеinsert
илиemplace
когда фактическая цель состоит в том, чтобы вставить элемент в карту.3. моя цель-вставить в карту и использовать после этого только структуру из карты.
4. В таком простом случае компилятор, скорее всего, предпочтет, чтобы вторая версия работала так же, как и первая. единственная проблема, которую я вижу в обеих версиях, заключается в том, что переменные-члены объекта неинициализированы.
5. В этом случае вам следует использовать std::map::emplace , который выполняет все это сразу. (правка, заметил тег c 14, поэтому рекомендовал совместимый метод)
Ответ №1:
Я полагаю, вы не хотите оставлять участников неинициализированными, поэтому давайте предоставим инициализаторы по умолчанию:
struct Ac
{
bool first = false;
bool second = false;
};
Далее, вы слишком часто ищете ключ. Сначала вы вызываете find
, и когда элемент был найден, вы вызываете operator[]
, который должен найти элемент снова. Вместо этого используйте итератор, возвращенный из find
.
Что касается вашего вопроса, наиболее очевидная разница заключается в том, что одна версия добавляет копию временной, в то время как другая вставляет копию локальной переменной. Если вам не нужна локальная переменная, вы можете вызвать emplace
, чтобы построить элемент на месте:
int main()
{
std::map<std::string, Ac> ma;
std::string st = "10.12";
auto ret = ma.emplace(st,Ac{});
if (! ret.second)
{
ret.first->second.first = true;
}
}
emplace
(аналогично insert
) возвращает pair
bool
значение, указывающее, имела ли место вставка, и итератор для элемента. ret.first->second.first
это может немного сбить с толку. ret.first
является ли итератор элемента на карте ret.first->second
Ac
частью этого элемента и ret.first->second.first
является его first
членом.
После оптимизации компилятора это, вероятно, так же эффективно, как и любая из ваших двух версий, но я считаю, что его более удобно использовать emplace
, когда вы хотите вставить элемент, и поскольку построение an Ac
сравнительно дешево, также не так уж плохо сконструировать временную так или иначе, не проверяя карту раньше. Чтобы быть действительно уверенным в том, что работает лучше, вам нужно будет измерить это. В общем, std::unordered_map
можно ожидать, что он будет быстрее, потому что его элементы не сортируются по ключам.
Комментарии:
1. спасибо за ваш ответ, если ma.emplace(st,Ac{}); используется со списком инициализаторов по умолчанию, все равно необходимо добавить bool first = false; значения обычно для членов структуры, не будет ли это сделано списком инициализаторов по умолчанию при вставке в карту ?
2. @BenRice что такое «список инициализаторов по умолчанию»? Извините, я не понимаю, что вы имеете в виду. В вашем коде элементы не инициализируются, если элемент еще не был на карте раньше
3. если я просто вставлю в карту только тогда,когда элемент для данного ключа не был найден с помощью ma.emplace(st, Ac{}); этого будет недостаточно ? Я имею в виду, что таким образом элементы переменного тока будут инициализированы
4. @BenRice из вашего комментария («могу ли я использовать список инициализаторов по умолчанию для false при вставке нового элемента в карту ?») Я предположил, что для вновь созданного элемента должен быть установлен элемент
false
. Если вы хотитеtrue
в обоих случаях, тоemplace
достаточно только одного. Хотя в вашем коде членыAc
не инициализируются (также нет, если вы используетеemplace(st,Ac{})
)5. Требуются два поиска, существуют разные ключи, используемые для
find
и[]
Ответ №2:
В (2) объект ac
существует до конца области, в то время как (1) создает временное, которое немедленно отбрасывается, когда оно больше не требуется.
Он также не содержит символа или ссылки, с помощью которых вы могли бы получить доступ к временному; вы можете получить доступ только к объекту, созданному в map
самом объекте, который является вашей заявленной целью.
Таким образом, на самом деле речь идет не о «лучшем«, а о том, что наиболее уместно в конкретной ситуации, поскольку они не являются семантически эквивалентными. В этом случае (1), однако, если вы хотите создать объект, выполните над ним некоторые операции, прежде чем поместить его на карту, потребуется (2).
Если вы выполнили над ним операции после копирования на карту, то ясно, что эти изменения будут касаться объекта, а не копии на карте.
Комментарии:
1. Спасибо @Clifford