Может ли диапазон, основанный на цикле, знать о конце? - программирование

Может ли диапазон, основанный на цикле, знать о конце?

Учитывая минимальный пример С++ 11 STL:

set<int> S = {1,2,3,4};
for(auto &x: S) {    
   cout << x;
   cout << ",";
}

Есть ли способ проверить, является ли x одним из них до самого конца? Целью этого примера является вывод 1,2,3,4, а не конечная запятая в конце. В настоящее время я использую стандарт для цикла с двумя итераторами,

set<int>::const_iterator itr;
set<int>::const_iterator penultimate_end_itr = --S.end();
for(itr=S.begin(); itr!=penultimate_end_itr;++itr) 
    cout << (*itr) << ',';
cout << (*penultimate_end_itr);

Что работает, но ужасно громоздко. Есть ли способ сделать проверку в цикле на основе диапазона?

EDIT:. Вопрос заключается не в том, чтобы распечатать список, разделенный запятыми. Я хочу знать, имеет ли цикл, основанный на диапазонах, какие-либо знания предпоследнего элемента в списке (т.е. Он один до конца). Минимальный пример был представлен таким образом, что у всех нас есть общий блок кода, о котором можно говорить.

4b9b3361

Ответ 1

Сама цель циклов, основанных на диапазонах, состоит в том, чтобы забыть итератор. Таким образом, они позволяют вам получить доступ к текущему значению, а не к итератору. Может ли следующий код сделать это для вас?

set<int> S = {1,2,3,4};

std::string output;
for(auto &x: S) {    
   if (!output.empty())
       output += ",";
    output += to_string(x);
  }

cout << output;

ИЗМЕНИТЬ

Другое решение: вместо сравнения итераторов (как и с обычными циклами), вы можете сравнить адреса значений:

set<int> S = {1,2,3,4};
auto &last = *(--S.end());
for (auto &x : S)
{
    cout << x;
    if (&x != &last)
        cout << ",";
}

Ответ 2

Boost.Range может помочь здесь:

if (std::begin(S) != std::end(S)) {
    std::cout << *std::begin(S);
    for (const auto &x: boost::make_iterator_range(std::next(std::begin(S)), std::end(S))) {
        std::cout << ", " << x;
    }
}

Гораздо более гибкий подход заключается в индексировании диапазона, используя boost::adaptors::indexed (начиная с Boost 1.56):

for (const auto &element: boost::adaptors::index(S)) {
    std::cout << (element.index() ? ", " : "") << element.value();
}

В версиях Boost до 1.56 boost::adaptors::indexed не будет работать, но вы можете легко написать произведение:

template <typename... T>
auto zip(const T&... ranges) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(ranges)...))>>
{
    auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(ranges)...));
    auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(ranges)...));
    return boost::make_iterator_range(zip_begin, zip_end);
}

template<typename T>
auto enumerate(const T &range) -> boost::iterator_range<boost::zip_iterator<boost::tuple<
    boost::counting_iterator<decltype(boost::distance(range))>, decltype(std::begin(range))>>>
{
    return zip(boost::make_iterator_range(boost::make_counting_iterator(0),
        boost::make_counting_iterator(boost::distance(range))), range);
}

for (const auto &tup: enumerate(S)) {
    std::cout << (tup.get<0>() ? ", " : "") << tup.get<1>();
}

Это используется функция zip из Функция Sequence-zip для С++ 11?