Единственное время (о котором я могу подумать), где operator[]
может быть полезным, это когда вы хотите установить значение ключа (перезаписать его, если оно уже имеет значение), и вы знаете, что это безопасно перезаписать (что и должно быть, так как вы должны использовать умные указатели, а не необработанные указатели) и дешевле для конструкции по умолчанию , а в некоторых контекстах значение должно иметь no-бросить конструкцию и присваивание .
например (похоже на ваш первый пример)
std::map<int, std::unique_ptr<int>> m;
m[3] = std::unique_ptr<int>(new int(5));
m[3] = std::unique_ptr<int>(new int(3)); // No, it should be 3.
В противном случае есть несколько способов сделать это в зависимости от контекста, однако я бы рекомендовал всегдаиспользуйте общее решение (таким образом, вы не сможете ошибиться).
Найдите значение и создайте его, если оно не существует:
1.Общее решение (рекомендуется, поскольку оно всегда работает)
std::map<int, std::unique_ptr<int>> m;
auto it = m.lower_bound(3);
if(it == std::end(m) || m.key_comp()(3, it->first))
it = m.insert(it, std::make_pair(3, std::unique_ptr<int>(new int(3)));
2.С дешевой конструкцией по умолчанию значения
std::map<int, std::unique_ptr<int>> m;
auto& obj = m[3]; // value is default constructed if it doesn't exists.
if(!obj)
{
try
{
obj = std::unique_ptr<int>(new int(3)); // default constructed value is overwritten.
}
catch(...)
{
m.erase(3);
throw;
}
}
3.С дешевой конструкцией по умолчанию и вставкой без бросков значения
std::map<int, my_objecct> m;
auto& obj = m[3]; // value is default constructed if it doesn't exists.
if(!obj)
obj = my_objecct(3);
Примечание: вы можете легко обернуть общее решение в вспомогательный метод:
template<typename T, typename F>
typename T::iterator find_or_create(T& m, const typename T::key_type& key, const F& factory)
{
auto it = m.lower_bound(key);
if(it == std::end(m) || m.key_comp()(key, it->first))
it = m.insert(it, std::make_pair(key, factory()));
return it;
}
int main()
{
std::map<int, std::unique_ptr<int>> m;
auto it = find_or_create(m, 3, []
{
return std::unique_ptr<int>(new int(3));
});
return 0;
}
Обратите внимание, что я передаюшаблонный фабричный метод вместо значения для случая создания, таким образом нет никаких накладных расходов, когда значение было найдено и его не нужно создавать.Поскольку лямбда-выражение передается в качестве аргумента шаблона, компилятор может включить его.