Как я могу проверить, включен ли я в последний элемент при повторении с использованием синтаксиса foreach

Например:

for( auto &iter: item_vector ) {
     if(not_on_the_last_element) printf(", ");
}

или

for( auto &iter: skill_level_map ) {
     if(not_on_the_last_element) printf(", ");
}

Ответ 1

Ты не можешь. Такова точка диапазона, что вам не нужны итераторы. Но вы можете просто изменить свою логику о том, как вы печатаете запятую, чтобы напечатать ее, если она не первая:

bool first = true;
for (auto& elem : item_vector) {
    if (!first) printf(", ");
    // print elem
    first = false;
}

Если это намерение цикла в любом случае. Или вы можете сравнить адреса:

for (auto& elem : item_vector) {
    if (&elem != &item_vector.back()) printf(", ");
    // ...
}

Ответ 2

Нет отличного метода. Но если у нас есть легкий доступ к последнему элементу контейнера...

std::vector<int> item_vector = ...;
for (auto & elem : item_vector) {
    ...
    if (&elem != &item_vector.back())
        printf(", ");
}

Ответ 3

Эти типы петель лучше всего пишутся с помощью Loop and half":

#include <iostream>
#include <vector>

int main()
{
    auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6};

    auto first = begin(somelist), last = end(somelist);
    if (first != last) {                // initial check
        while (true) {
            std::cout << *first++;     
            if (first == last) break;   // check in the middle
            std::cout << ", ";
        }
    }
}

Живой пример, который печатает

1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 6

то есть. без разделителя в конце последнего элемента.

Проверка в середине - это то, что делает это отличным от do-while (check up front) или for_each/range-based (для проверки в конце). Попытка принудительного циклического цикла на этих циклах вводит либо дополнительные условные ветки, либо дублирующую программную логику.

Ответ 4

безопасно сохраните этот код в файле заголовка в вашем маленьком пакете утилит:

namespace detail {
    template<class Iter>
    struct sequence_emitter
    {
        sequence_emitter(Iter first, Iter last, std::string sep)
        : _first(std::move(first))
        , _last(std::move(last))
        , _sep(std::move(sep))
        {}

        void write(std::ostream& os) const {
            bool first_element = true;
            for (auto current = _first ; current != _last ; ++current, first_element = false)
            {
                if (!first_element)
                    os << _sep;
                os << *current;
            }
        }

    private:
        Iter _first, _last;
        std::string _sep;
    };

    template<class Iter>
    std::ostream& operator<<(std::ostream& os, const sequence_emitter<Iter>& se) {
        se.write(os);
        return os;
    }
}

template<class Iter>
detail::sequence_emitter<Iter>
emit_sequence(Iter first, Iter last, std::string separator = ", ")
{
    return detail::sequence_emitter<Iter>(std::move(first), std::move(last), std::move(separator));
}

то вы можете испускать любой диапазон любого контейнера без трейлинг-разделителя следующим образом:

vector<int> x { 0, 1, 2, 3, 4, 5 };
cout << emit_sequence(begin(x), end(x)) << endl;

set<string> s { "foo", "bar", "baz" };

cout << emit_sequence(begin(s), end(s), " comes before ") << endl;

ожидаемый вывод:

0, 1, 2, 3, 4, 5
bar comes before baz comes before foo

Ответ 5

Это похоже на шаблон состояния.

#include <iostream>
#include <vector>
#include <functional>

int main() {
    std::vector<int> example = {1,2,3,4,5};

    typedef std::function<void(void)> Call;
    Call f = [](){};
    Call printComma = [](){ std::cout << ", "; };
    Call noPrint = [&](){ f=printComma; };
    f = noPrint;

    for(const auto& e:example){
        f();
        std::cout << e;
    }

    return 0;
}


Output:

1, 2, 3, 4, 5

Первый раз через f указывает на noPrint, который служит только для f, а затем указывает на printComma, поэтому запятые только печатаются перед вторым и последующим элементами.

Ответ 6

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

auto end = vector.end() - 1;
for (auto iter = vector.begin(); iter != end; ++iter) {
   // do your thing
   printf(", ");
}
// do your thing for the last element

Если вы не хотите повторять код дважды, чтобы "сделать свою вещь", как я бы хотел, тогда создайте лямбду, которая делает это:

auto end = vector.end() - 1;
// create lambda
for (auto iter = vector.begin(); iter != end; ++iter) {
   lambda(*iter);
   printf(", ");
}
lambda(vector.back());