[英]Efficient substitute for std::map::insert_or_assign with hint
我正在嘗試為不支持 C++17 的構建環境編寫一個帶有hint
參數的std::map::insert_or_assign
的替代品。
我希望這個替代品同樣有效,並且不需要映射類型為DefaultConstructible 。 后一個要求排除了map[key] = value
。
我想出了這個:
template <class M, class K, class T>
typename M::iterator insert_or_assign(M& map, typename M::const_iterator hint,
K&& key, T&& value)
{
using std::forward;
auto old_size = map.size();
auto iter = map.emplace_hint(hint, forward<K>(key), forward<T>(value));
// If the map didn't grow, the key already already existed and we can directly
// assign its associated value.
if (map.size() == old_size)
iter->second = std::forward<T>(value);
return iter;
}
但是,我不知道我是否可以相信std::map
在密鑰已經存在的情況下不會移動分配值兩次。 這安全嗎? 如果沒有,是否有一種安全的方法可以有效地實現std::map::insert_or_assign
的替代品,並采用hint
參數?
根據 NathanOliver 的評論,他引用了std::map::emplace
的 cppreference文檔:
即使容器中已經有一個帶有鍵的元素,也可以構造該元素,在這種情況下,新構造的元素將立即被銷毀。
如果我們假設這同樣適用於std::map::emplace_hint
,那么在我在問題中提出的解決方案中,該值可能會過早地消失。
我想出了另一個解決方案(未測試),它只forward
s 值一次。 我承認它不漂亮。 :-)
// Take 'hint' as a mutating iterator to avoid an O(N) conversion.
template <class M, class K, class T>
typename M::iterator insert_or_assign(M& map, typename M::iterator hint,
K&& key, T&& value)
{
using std::forward;
#ifdef __cpp_lib_map_try_emplace
return map.insert_or_assign(hint, forward<K>(key), forward<T>(value);
#else
// Check if the given key goes between `hint` and the entry just before
// hint. If not, check if the given key matches the entry just before hint.
if (hint != map.begin())
{
auto previous = hint;
--previous; // O(1)
auto comp = map.key_comp();
if (comp(previous->first, key)) // key follows previous
{
if (comp(key, hint->first)) // key precedes hint
{
// Should be O(1)
return map.emplace_hint(hint, forward<K>(key),
forward<T>(value));
}
}
else if (!comp(key, previous->first)) // key equals previous
{
previous->second = forward<T>(value); // O(1)
return previous;
}
}
// If this is reached, then the hint has failed.
// Check if key already exists. If so, assign its associated value.
// If not, emplace the new key-value pair.
auto iter = map.find(key); // O(log(N))
if (iter != map.end())
iter->second = forward<T>(value);
else
iter = map.emplace(forward<K>(key), forward<T>(value)); // O(log(N))
return iter;
#endif
}
我希望其他人會提出一個更好的解決方案!
請注意,在訴諸這種丑陋的混亂之前,我檢查了__cpp_lib_map_try_emplace
功能測試宏以測試是否支持std::map::insert_or_assign
。
編輯:刪除了在嘗試檢查密鑰是否已存在於hint
時的緩慢迭代器算術愚蠢。
編輯 2 : hint
現在被視為一個變異迭代器,以避免昂貴的 O(N) 轉換,如果它被作為一個const_iterator
傳遞。 這允許我手動檢查提示並在提示成功時執行 O(1) 插入或賦值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.