【问题标题】:Renaming first and second of a map iterator重命名地图迭代器的第一个和第二个
【发布时间】:2010-12-02 18:32:14
【问题描述】:

有没有办法重命名映射迭代器的第一个和第二个访问器函数。我知道它们之所以有这些名称是因为底层对表示键和值,但我希望迭代器更具可读性。我认为这可能使用迭代器适配器,但我不确定如何实现它。

请注意,我不能使用 boost。

我的意思的例子:

map<Vertex, Edge> adjacency_list;
for(map<Vertex, Edge>::iterator it = adjacency_list.begin();
    it != adjacency_list.end();
    ++it)
{
    Vertex v = it->first;
    //instead I would like to have it->vertex
}

【问题讨论】:

标签: c++ stl map iterator


【解决方案1】:

如果您只关心可读性,您可以这样做:

typedef map<Vertex, Edge> AdjacencyList;
struct adjacency
{
    adjacency(AdjacencyList::iterator& it) 
      : vertex(it->first), edge(it->second) {}
    Vertex& vertex;
    Edge& edge;
};

然后:

Vertex v = adjacency(it).vertex;

【讨论】:

    【解决方案2】:

    您不能重命名成员,但您可以使用一些功能来提供帮助。

    inline Vertex& vertex(map<Vertex, Edge>::iterator& it) {return it->first;}
    inline Edge& edge(map<Vertex, Edge>::iterator& it) {return it->second;}
    

    然后,您可以使用vertex(it),而不是您想要的it-&gt;vertex

    【讨论】:

      【解决方案3】:

      很遗憾,没有。我通常做的是这样的:

      typedef map<Vertex, Edge> AdjacencyList;
      typedef AdjacencyList::value_type Vertex_Edge_Pair;
      

      为了可读性。在你的循环中,你也可以说

      Vertex& current_vertex = it->first;
      Edge& current_edge = it->second;
      

      【讨论】:

        【解决方案4】:

        当然,重新实现或包装迭代器,但值得付出努力吗?不会

        Vertex& v = it->first;
        

        更容易吗?

        【讨论】:

        • 另外请记住,使用 std::map 迭代器的第一个和第二个是一种常见的习惯用法,以至于您会混淆阅读您的代码的任何人 - 有点。
        • 确实如此,但从理智的角度来看,firstsecond 已经够混乱了 ;-)
        【解决方案5】:

        如果您不需要迭代器(例如,基于范围的 for 循环适合您的目的),那么从 c++17 开始,您可以使用 结构化绑定

        map<Vertex, Edge> adjacency_list;
        for( auto & [ vertex, edge ] : adjacency_list )
        {
            // do stuff with vertex
        }
        

        【讨论】:

          【解决方案6】:

          我不建议真正使用它,但它似乎确实有效,至少在测试程序执行我想要/预期的最低程度:

          #include <map>
          #include <string>
          #include <iostream>
          
          template <class T, class U>
          struct my_pair : public std::pair<T, U> {
              T const &vertex;
              my_pair(std::pair<T, U> const &x) : std::pair<T, U>(x), vertex(x.first) { }
          };
          
          template <class T, class U>
          struct my_map : public std::map<T, U> { 
              my_pair<T, U> find(T const &t) { return my_pair<T, U>(*std::map<T,U>::find(t)); }
          };
          
          class Vertex { 
              int x;
          public:
              Vertex(int v) : x(v) {}
              bool operator<(Vertex const &other) const { return x < other.x; }
              friend std::ostream &operator<<(std::ostream &os, Vertex const &v) { return os << v.x; }
          };
          
          int main() { 
              my_map<Vertex, std::string> m;
          
              m[1] = "This is it";
          
              my_pair<Vertex, std::string> mp = m.find(1);
              std::cout << mp.vertex << ": " << mp.second;
              return 0;
          }
          

          【讨论】:

            【解决方案7】:

            我喜欢 KeithB 的免费功能解决方案。但是,一个更可重用的解决方案可能会更好。

            第一个或第二个访问的函数对象怎么样,因为您可以为实例命名任何您喜欢的名称:

            #include <map>
            #include <string>
            #include <iostream>
            
            struct GetFirst
            {
                template <class First, class Second>
                First& operator()(std::pair<First, Second>& p)
                {
                    return p.first;
                }
            
                template <class First, class Second>
                const First& operator()(const std::pair<First, Second>& p)
                {
                    return p.first;
                }
            };
            
            struct GetSecond
            {
                template <class First, class Second>
                Second& operator()(std::pair<First, Second>& p)
                {
                    return p.second;
                }
            
                template <class First, class Second>
                const Second& operator()(const std::pair<First, Second>& p)
                {
                    return p.second;
                }
            };
            
            int main()
            {
                typedef std::map<std::string, int> Map;
            
                Map persons;
                persons["John"] = 20;
                persons["Mary"] = 24;
            
                //create named accessors
                GetFirst name;
                GetSecond age;
            
                for (Map::iterator it = persons.begin(); it != persons.end(); ++it) {
                    std::cout << name(*it) << " is aging.\n";
                    ++age(*it);
                }
            
                for (Map::const_iterator it = persons.begin(); it != persons.end(); ++it) {
                    std::cout << "Name: " << name(*it) << ", age: " << age(*it) << '\n';
                }
            }
            

            这是我能做的最好的。我也试图让那些函子直接接受迭代器,但一种或另一种方式意味着签名将包含依赖名称,这显然使得模板类型推导成为不可能(即使使用C++0x 的延迟返回类型)。

            【讨论】:

              【解决方案8】:

              我有一个邪恶的解决方案!

              #define vertex first
              #define edge second
              

              虽然作为一种邪恶的解决方案,当你在别处不小心使用这些词时,它无疑会造成巨大的创伤和难以诊断编译问题。

              为了完整性而添加。

              不敢相信没有其他人提出过这个建议。

              【讨论】:

              • 没有其他人建议这样做,因为这是一个糟糕的主意。
              • 为什么没有人建议这样做?
              猜你喜欢
              • 2019-04-26
              • 2014-07-26
              • 1970-01-01
              • 2012-08-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-04
              相关资源
              最近更新 更多