Как создать итератор std :: list в цикле с приращением - PullRequest
7 голосов
/ 16 февраля 2010

Я пытаюсь сделать двойной цикл над std :: list для работы с каждой парой элементов. Однако у меня возникли некоторые проблемы при инициализации второго итератора. Код, который я хотел бы написать:

for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i) {
    for(std::list<int>::iterator j = i+1; j != l.end(); ++j) {
        ...
    }
}

Это не работает, потому что итераторы списка не имеют произвольного доступа, поэтому вы не можете сделать +1. Но у меня возникли проблемы с поиском аккуратной альтернативы; Компилятор, кажется, не очень доволен std::list<int>::iterator j(i)++;, на который я надеялся. Кажется, что для достижения того, что я хочу, мне понадобится какое-то неловкое дополнительное приращение, которое не вписывается в структуру цикла for.

Существуют очевидные альтернативы (например, с использованием вектора!), Но мне кажется, что должен быть какой-то достаточно аккуратный способ сделать это, чего я сейчас не вижу.

Заранее спасибо за любую помощь:)

Ответы [ 7 ]

9 голосов
/ 16 февраля 2010
for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i) {
    std::list<int>::iterator j = i;
    for(std::advance(j, 1); j != l.end(); ++j) {
        ...
    }
}
8 голосов
/ 16 февраля 2010

Как насчет:

for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) {
    for (std::list<int>::iterator j = i; ++j != l.end(); ) {
        // ...
    }
}
4 голосов
/ 16 февраля 2010
for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i) {
    std::list<int>::iterator j = i; ++j;
    for(; j != l.end(); ++j) {
        ...
    }
}

Вернуться в игру!

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

2 голосов
/ 16 февраля 2010

Простая «аккуратная» альтернатива может основываться на том факте, что итератор списка является объектом пользовательского типа с перегруженными операторами (в отличие от встроенного типа). (Конечно, это формально не гарантировано, но можно ожидать, что это основано на природе контейнера списка.) По этой причине возможно применить перегруженный оператор префикса ++ к временному объекту типа итератора списка.

Чтобы достичь желаемого, вам просто нужно создать временную копию i, увеличить ее с помощью префикса ++, а затем использовать результирующее значение для инициализации j

for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i) { 
  for(std::list<int>::iterator j = ++std::list<int>::iterator(i); j != l.end(); ++j) { 
    ... 
  } 
} 

И это все. Обратите внимание, что этот прием довольно популярен и может встречаться в реальном коде время от времени. Также обратите внимание, что он обычно не будет работать с std::vector, потому что многие реализации используют обычные встроенные указатели в качестве векторных итераторов, но обычно он будет работать с std::list.

Однако лично я бы не стал использовать это в своем коде. Вы уже получили несколько хороших ответов, которые делают это, добавив дополнительную строку кода.

2 голосов
/ 16 февраля 2010

Я просто отказываюсь от идеи, которая была у меня в ответе Диркгентли:

template <typename Iter, typename Dist>
Iter advance_copy(Iter pIter, const Dist& pOffset)
{
    std::advance(pIter, pOffset);

    return pIter;
}

// ...

typedef std::list<int> int_list;

for(int_list::iterator i = l.begin(); i != l.end(); ++i)
{
    for(int_list::iterator j = advance_copy(i, 1); j != l.end(); ++j)
    {
    }
}

Вы также можете создать другой класс служебных функций, чтобы сделать его кратким:

// for consistency,
template <typename Iter>
void increment(Iter& pIter)
{
    ++pIter;
}

template <typename Iter>
Iter increment_copy(Iter pIter)
{
    return ++pIter;
}

// ...

typedef std::list<int> int_list;

for(int_list::iterator i = l.begin(); i != l.end(); ++i)
{
    for(int_list::iterator j = increment_copy(i); j != l.end(); ++j)
    {
    }
}
1 голос
/ 16 февраля 2010

Я бы пошел за предложением Шона, за исключением того, чтобы сделать цикл while:

for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) {
    std::list<int>::iterator j( i ); 
    while( ++j != l.end() ) {
        // ...
    }
}
0 голосов
/ 16 февраля 2010

Если вы уже используете Boost, то самый простой подход - использовать boost::next.

for(std::list<int>::iterator i = l.begin(); i != l.end(); ++i)
    for(std::list<int>::iterator j = boost::next(i); j != l.end(); ++j)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...