Переименование первого и второго итератора карты - PullRequest
17 голосов
/ 30 сентября 2009

Есть ли способ переименовать первую и вторую функции доступа итератора карты. Я понимаю, что у них есть эти имена из-за базовой пары, которая представляет ключ и значение, но я хотел бы, чтобы итераторы были немного более читабельными. Я думаю, что это возможно при использовании адаптера итератора, но я не уверен, как это реализовать.

Обратите внимание, что я не могу использовать повышение.

Пример того, что я имею в виду:

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
}

Ответы [ 8 ]

19 голосов
/ 30 сентября 2009

Если вас беспокоит читабельность, вы можете сделать что-то вроде этого:

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;
12 голосов
/ 01 октября 2009

Вы не можете переименовать участников, но у вас могут быть некоторые функции, чтобы помочь.

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

Тогда вместо it->vertex, как вы хотите, вы можете сделать vertex(it)

7 голосов
/ 30 сентября 2009

К сожалению, нет. Что я обычно делаю, это:

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

Для удобства чтения. Внутри вашей петли вы также можете сказать

Vertex& current_vertex = it->first;
Edge& current_edge = it->second;
6 голосов
/ 30 сентября 2009

Конечно, переопределение или перенос итератора, но стоит ли это усилий? Не было бы

Vertex& v = it->first;

быть проще?

1 голос
/ 02 октября 2009

Мне понравилось решение 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';
    }
}

Это лучшее, что я мог сделать. Я также пытался заставить эти функторы напрямую принимать итератор, но это так или иначе означает, что подпись будет содержать зависимые имена, что, очевидно, делает невозможным вывод типа шаблона (я не смог найти способ перегрузить GetSecond для iterator/const_iterator даже при тип отложенного возврата C ++ 0x).

1 голос
/ 01 октября 2009

Я бы не рекомендовал действительно использовать это, но, похоже, оно работает, по крайней мере, до минимальной степени, когда тестовая программа делает то, что я хотел / ожидал:

#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;
}
0 голосов
/ 28 мая 2018

Если вам не нужен итератор (например, цикл for на основе диапазона соответствует вашим целям), то с c ++ 17 вы можете использовать структурированные привязки :

map<Vertex, Edge> adjacency_list;
for( auto & [ vertex, edge ] : adjacency_list )
{
    // do stuff with vertex
}
0 голосов
/ 01 октября 2014

У меня есть злое решение!

#define vertex first
#define edge second

Хотя, как злое решение, оно, несомненно, вызовет большую травму и затруднит диагностику проблем компиляции, когда вы случайно используете эти слова в другом месте.

Добавлено для полноты.

Не могу поверить, что никто другой не предложил это.

...