What is the idiomatic way to iterate a container while incrementing an integer index?

My personal preference: just keep the extra index. It’s clear as it is, and in case you ever have an if() inside the loop, you can also easily skip the count:

std::list<std::string> items;
{
    int i = 0;
    for (auto & item : items) {
        if (some_condition(item)) {
            item += std::to_string(i); // mark item as the i-th match
            ++i;
        }
    }
}

Just make sure to keep the i counter close near the loop, using extra { } to create a nested scope. Also, the post-increment is borderline unclear.

Alternatives: I would like a ranged-based index_for language construct that would provide an automatic counter i, but alas, that’s not the case currently.

However, if you absolutely, positively, definitively insist on some nice wrapper, it’s actually instructive to look at the semantics of your loop, which is that of a std::transform with a pair of std::list iterators and a boost::counting_iterator.

std::transform(
    begin(items), end(items), 
    boost::counting_iterator<int>(0), 
    begin(items), 
    [](auto const& elem, auto i) {
    return elem + std::to_string(i);    
});

This 4-legged overload of std::transform is sometimes called zip_with, which is why there are some comments to use a boost::zip_iterator with the list and counting_iterator.

You can make some nice range-based wrapper to be a lot more concise:

template<class Range, class T, class BinaryOp>
void self_transform(Range& rng, T value, BinaryOp op)
{
    auto i = value;
    for (auto& elem : rng) {
        elem = op(elem, i);        
        ++i;
    }
}

that can be more compactly called like:

self_transform(items, 0, [](auto const& elem, auto i) {
    return elem + std::to_string(i);    
});

Live Example.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)