std::list iterator: получить следующий элемент

Я пытаюсь создать строку, используя элементы данных, хранящиеся в std::list, где я хочу, чтобы запятые помещались только между элементами (т.е. если элементы {A,B,C,D} в списке, результирующая строка должна быть «А, Б, В, Г».

Этот код не работает:

typedef std::list< shared_ptr<EventDataItem> > DataItemList;
// ...
std::string Compose(DataItemList& dilList)
{
    std::stringstream ssDataSegment;
    for(iterItems = dilList.begin();
        iterItems != dilList.end(); 
        iterItems++)
    {
        // Lookahead in list to see if next element is end
        if((iterItems + 1) == dilList.end())  
        {
            ssDataSegment << (*iterItems)->ToString();
        }
        else
        {
            ssDataSegment << (*iterItems)->ToString() << ",";
        }
    }
    return ssDataSegment.str();
}

Как получить "следующий элемент" в std::list с помощью итератора? Я ожидаю, что это связанный список, почему я не могу перейти к следующему элементу?


person J. Polfer    schedule 23.04.2010    source источник
comment
У вас также есть ошибка в цикле for: iterItems = dilList.end(); должно быть iterItems != dilList.end();.   -  person Fred Larson    schedule 23.04.2010
comment
@ Фред - Верно. Спасибо что подметил это.   -  person J. Polfer    schedule 23.04.2010
comment
Возможный дубликат: stackoverflow.com /вопросы/3673684/   -  person Luis Andrés García    schedule 17.04.2013


Ответы (6)


Вы не можете сделать it + N, потому что у вас нет произвольного доступа к итераторам списка. Вы можете выполнять только один шаг за раз с итераторами списка (это двунаправленные итераторы).

Вы можете использовать boost::next и boost::prior

// Lookahead in list to see if next element is end
if(boost::next(iterItems) == dilList.end())  
{

Или вы можете напечатать запятую перед:

std::string Compose(DataItemList& dilList)
{
    std::stringstream ssDataSegment;
    for(iterItems = dilList.begin();
        iterItems != dilList.end(); 
        ++iterItems)
    {
        if(iterItems != diList.begin())
            ssDataSegment << ",";
        ssDataSegment << (*iterItems)->ToString();
    }
    return ssDataSegment.str();
}
person Johannes Schaub - litb    schedule 23.04.2010
comment
Примечание: next() и prev() находятся в файле boost/utility.hpp. Если они не переедут куда-то еще. - person J. Polfer; 23.04.2010
comment
Технически это boost::prior(). - person J. Polfer; 23.04.2010

Я считаю, что итератор списка является двунаправленным, но не произвольным доступом. Это означает, что вы можете делать ++ и --, но не добавлять и не вычитать.

Чтобы получить следующий итератор, сделайте копию и увеличьте его.

person Zan Lynx    schedule 23.04.2010

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

std::string Compose(DataItemList& dilList)
{
    std::stringstream ssDataSegment;
    for(iterItems = dilList.begin();
        iterItems != dilList.end(); 
        ++iterItems)
    {
        // See if current element is the first
        if(iterItems == dilList.begin())  
        {
            ssDataSegment << (*iterItems)->ToString();
        }
        else
        {
            ssDataSegment << "," << (*iterItems)->ToString();
        }
    }
    return ssDataSegment.str();
}
person Fred Larson    schedule 23.04.2010
comment
Альтернативный подход заключается в том, чтобы убедиться, что контейнер не пуст, напечатать первый элемент, увеличить итератор, а цикл просто печатает ваш else и не требует if/else. - person Mark B; 23.04.2010
comment
@Mark B: Да, я видел, что Йоханнес сделал именно это (я не видел этого в его ответе, когда публиковал свой). Это немного опрятнее. - person Fred Larson; 23.04.2010

Вы можете полностью избежать этой проблемы, используя:

std::string Compose(DataItemList& dilList)
{
    std::stringstream ssDataSegment;
    for(iterItems = dilList.begin(); iterItems != dilList.end(); iterItems++)
    {
        ssDataSegment << (*iterItems)->ToString() << ","; // always write ","
    }
    std::string result = ssDataSegment.str();
    return result.substr(0, result.length()-1); // skip the last ","
}

Сначала вы пишете «,» для всех элементов (даже для последнего). Затем вы удаляете ненужные последние «,», используя substr. Это дополнительно приводит к более четкому коду.

person Danvil    schedule 23.04.2010
comment
Всегда +1 к более чистому решению. ;-) - person DevSolar; 23.04.2010
comment
Я не согласен с тем, что это более четкий код. Никогда не бывает так просто выполнять посторонние операции, а затем отменять их позже. Кроме того, это не идиоматично, поскольку идиоматический способ состоит в том, чтобы рассматривать первую итерацию как особенную. -1 - person rmeador; 23.04.2010
comment
Это примерно в два раза короче других решений и по-прежнему того же порядка с точки зрения эффективности. Я, лично, считаю, что чище. Кроме того, если мы говорим о чистой прибыли от операций, то ДРУГИЕ решения имеют более высокую чистую прибыль. - person Adam; 29.01.2012

Еще одна возможность:

#include "infix_iterator.h"
#include <sstream>

typedef std::list<shared_ptr<EventDataItem> > DataItemList;

std::string Compose(DataItemList const &diList) {
    std::ostringstream ret;
    infix_ostream_iterator out(ret, ",");

    for (item = diList.begin(); item != diList.end(); ++item)
        *out++ = (*item)->ToString();
    return ret.str();
}

Вы можете получить infix_iterator.h из архива Usenet Google (или различные веб-сайты).

person Jerry Coffin    schedule 23.04.2010

Примечание. Начиная с C++11, вы можете использовать std::next и std::prev.

person user1823890    schedule 15.01.2015