为什么 std::map 不提供key_iterator和value_iterator

Why doesn’t std::map provide key_iterator and value_iterator?

本文关键字:iterator value key std map 为什么      更新时间:2023-10-16

我在 C++03 环境中工作,将函数应用于映射的每个键是很多代码:

const std::map<X,Y>::const_iterator end = m_map.end();
for (std::map<X,Y>::const_iterator element = m_map.begin(); element != end; ++element)
{
   func( element->first );
}

如果存在key_iterator,则相同的代码可以利用std::for_each

std::for_each( m_map.key_begin(), m_map.key_end(), &func );

那么为什么不提供呢?有没有办法使第一种模式适应第二种模式?

是的,这是一个愚蠢的缺点。 但它很容易纠正:您可以编写自己的泛型key_iterator类,该类可以从map(对)迭代器构造。 我已经做到了,它只是几行代码,然后制作value_iterator也是微不足道的。

std::map<K, V>不需要为键和/或值提供迭代器:这样的迭代器可以很容易地基于现有的迭代器构建。好吧,这并不像应该/可能的那样容易,但它肯定是可行的。我知道 Boost 有一个迭代器适配器库。

真正的问题可能是:为什么标准C++库不提供迭代器适配器来项目迭代器?在我看来,简短的回答是:因为,一般来说,你不想修改迭代器来选择访问的属性!您宁愿希望投影或更一般地转换访问的值,但仍保持相同的位置概念。表述不同,我认为有必要将定位的概念(即推进迭代器并测试其位置是否有效)与访问给定位置的属性分开。我设想的方法看起来像这样:

std::for_each(m_map.key_pm(), m_map.begin(), m_map.end(), &func);

或者,如果您知道从映射的迭代器获得的底层结构是一个std::pair<K const, V>(就像std::map<K, V>的情况一样,但不一定是类似于关联容器的其他容器的情况;例如,基于 B 树的关联容器将受益于将键和值拆分为单独的实体):

std::for_each(_1st, m_map.begin(), m_map.end(), &func);

我的 STL 2.0 页面是一篇 [不完整] 的文章,其中包含更多关于我认为应该如何改进标准C++库算法的细节,包括上述将迭代器分为定位(游标)和属性访问(属性映射)。

那么为什么不提供呢?

我不知道。

有没有办法使第一种模式适应第二种模式?

除了制作"键迭代器"(参见我的评论和其他答案)之外,您还可以围绕func编写一个小包装器,例如:

class FuncOnFirst { // (maybe find a better name)
public:
    void operator()(std::map<X,Y>::value_type const& e) const { func(e.first); }
};

然后使用:

std::for_each( m_map.begin(), m_map.end(), FuncOnFirst() );

稍微通用一点的包装器:

class FuncOnFirst { // (maybe find a better name)
public:
    template<typename T, typename U>
    void operator()(std::pair<T, U> const& p) const { func(p.first); }
};
不需要

key_iteratorvalue_iterator,因为std::mapvalue_type是一个std::pair<const X, Y>,这就是for_each()调用的函数(或函子)将要操作的内容。 单个迭代器不会提高性能,因为pair聚合在映射使用的二叉树的基础节点中。

通过std::pair访问键和值几乎不费力。

#include <iostream>
#include <map>
typedef std::map<unsigned, unsigned> Map;
void F(const Map::value_type &v)
{
    std::cout << "Key: " << v.first << " Value: " << v.second << std::endl;
}
int main(int argc, const char * argv[])
{
    Map map;
    map.insert(std::make_pair(10, 20));
    map.insert(std::make_pair(43, 10));
    map.insert(std::make_pair(5, 55));
    std::for_each(map.begin(), map.end(), F);
    return 0;
}

这给出了输出:

Key: 5 Value: 55
Key: 10 Value: 20
Key: 43 Value: 10
Program ended with exit code: 0