【问题标题】:std::map insert() hint location: difference between c++98 and c++11std::map insert() 提示位置:c++98 和 c++11 的区别
【发布时间】:2015-09-24 10:02:11
【问题描述】:

cplusplus' entry on map::insert() 上,我读到了一个可以添加作为函数提示的位置,即“如果position 指向将插入元素之前的元素,则该函数优化其插入时间" 对于 c++98,而对于 c++11,优化发生 "如果 position 指向将 跟随 插入元素的元素(或者到最后,如果它是最后一个)”。

这是否意味着以下形式的代码 sn-ps 的性能(在我正在处理的遗留代码中很丰富,并以 Scott Meyer 的“有效 STL”为模型,第 24 项)在切换到符合 C++11 的编译器?

auto pLoc = someMap.lower_bound(someKey);
if(pLoc != someMap.end() && !(someMap.key_comp()(someKey, pLoc->first)))
    return pLoc->second;
else
    auto newValue = expensiveCalculation();
    someMap.insert(pLoc, make_pair(someKey, newValue));  // using the lower bound as hint
    return newValue;

改进此模式以用于 C++11 的最佳方法是什么?

【问题讨论】:

  • 这是一个奇妙的不兼容性。干得好,C++。
  • LWG issue 233N1780。我不确定是否存在任何实际实现 C++98 规范的实现。
  • @LightnessRacesinOrbit:那么您是否更喜欢与 C++98 兼容的 bug,就像 Wine 对 Windows API 的承诺一样?有一次,Windows 中存在另一个严重的安全漏洞,Wine 中也发现了同样的漏洞,这当然是相同 API 规范的完全不同的实现。这有点令人印象深刻,但我宁愿 ISO 修复 C++98 中的缺陷,而不是将它们传播到永恒。
  • @ArneVogel:我在一定程度上支持你,但这种特殊变化潜入的方式似乎不可接受。它不仅默默地破坏了早期的程序,而且它也是一个非常微妙的变化,我怀疑很多人甚至会注意到。这似乎与 C++ 项目的既定目标大相径庭,这是一种上升趋势。不过请不要误会:我没有更好的建议。
  • @LightnessRacesinOrbit 是否有针对 C++98 规范编写的实际“早期程序”?因为这需要使用,比如说,lower_bound,然后递减返回的迭代器——在检查它不是begin()之后,否则它将是UB。

标签: c++ c++11 dictionary insert


【解决方案1】:

C++98 规范是标准中的一个缺陷。请参阅LWG issue 233N1780 中的讨论。

回想一下,lower_bound 将迭代器返回到第一个 key 不小于指定 key 的元素,而 upper_bound 返回一个迭代器到第一个 key 大于指定 key 的元素。如果容器中没有与指定键等效的键,则 lower_boundupper_bound 返回相同的内容 - 一个指向元素的迭代器,如果它在地图。

因此,换句话说,您当前的代码在 C++11 规范下已经可以正常工作,而实际上在 C++98 的缺陷规范下是错误的。

【讨论】:

  • 很好的答案!我只想补充一点,当已知新密钥小于任何现有密钥(即插入到前面)时,旧规范不允许提供提示,而 end() 对任何事情都没有用。修复后,提示迭代器的工作方式与传递给序列容器 insert() 的迭代器相同,从而解决了此问题。
  • @ArneVogel 是的,这是在 N1780 开头提到的。
【解决方案2】:

是的,它会影响复杂性。给出正确的提示将使insert()具有摊销常数复杂度,而给出和不正确的提示将迫使地图从头开始搜索位置,给出对数复杂度。基本上,无论您的地图有多大,一个好的提示都会使插入在恒定时间内发生;有一个不好的提示,在较大的地图上插入会更慢。

显然,解决方案是使用upper_bound 而不是lower_bound 搜索提示。

【讨论】:

    【解决方案3】:

    我认为正确的 C++11 风格提示插入可能如下:

    iterator it = table.upper_bound(key);   //upper_bound returns an iterator pointing to the first element that is greater than key
    if (it == table.begin() || (--it)->first < key) {
        // key not found path
        table.insert(it, make_pair(key, value));
    }
    else {
        // key found path
        it->second = value;
    }
    

    【讨论】:

    • 如果table 为空,这将崩溃。
    【解决方案4】:

    工作 lambda 函数的快照供您参考。 注意:m_map 不能为空。如果地图为空,则很容易知道在哪里添加元素。

        auto create_or_get_iter = [this] (const K& key) {
    
            auto it_upper = m_map.upper_bound(key);
            auto it_effective = it_upper == m_map.begin() ? it_upper : std::prev(it_upper);
            auto init_val = it_effective->second;
    
            if (it_effective == m_map.begin() || it_effective->first < key) {
                return m_map.insert(it_effective, std::make_pair(key, init_val));
            } else {
                it_effective->second = init_val;
                return it_effective;
            }
    
        };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-20
      • 1970-01-01
      • 2011-03-24
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多