簡體   English   中英

帶提示的 std::map::insert_or_assign 的有效替代品

[英]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時的緩慢迭代器算術愚蠢。


編輯 2hint現在被視為一個變異迭代器,以避免昂貴的 O(N) 轉換,如果它被作為一個const_iterator傳遞。 這允許我手動檢查提示並在提示成功時執行 O(1) 插入或賦值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM