1

I wonder weather it is possible to use the the c++11 ranged-based-for-loop syntax to search for an insertion point that is later used in i.e. a list. Can I compact the for (auto i = l.begin(); i != l.end(); i++) in some way by using the c++11 ranged-based-for-loop syntax instead?

#include <iostream>
#include <list>

int main(int argc, char **argv) {

    std::list<int> l = { 1, 2, 3, 5};

    for (auto i = l.begin(); i != l.end(); i++) {
        if (*i == 3) {
            l.insert(i, 4);
            break;
        }
    }

    for (auto &i : l) {
       std::cout << " " << i;
    }
};

In pseudocode something like:

for (auto &i : l) {
    if (i == 3) {
        l.insert(some_magic_opertor(i), 4);
        break;
    }
}

or in pseudocode:

typedef std::list<int>::iterator it;
for (it i : l) {
    if (*i == 3) {
        l.insert(i, 4);
        break;
    }
}
Konrad Eisele
  • 3,088
  • 20
  • 35

2 Answers2

1

This would only be possible if you create your own fancy iterator adapter that would return the underlying iterator when dereferenced:

template <typename Iterator>
class exposed_iterator
{
public:
    exposed_iterator(Iterator it)
        : m_iterator(std::move(it)) { }
    Iterator &operator*()
    {
        return m_iterator;
    }

    exposed_iterator & operator++()
    {
        ++m_iterator;
        return *this;
    }

    bool operator!=(const exposed_iterator &that) const
    {
        return m_iterator != that.m_iterator;
    }

private:
    Iterator m_iterator;
};

And then provide a function and the inconveniently missing from the standard range class to use in range-based for loop:

template <typename Iterator>
struct range //struct for the sake of simplicity
{
    Iterator m_begin;
    Iterator m_end;

    Iterator begin() const { return m_begin; }
    Iterator end() const { return m_end; }
};

template <typename Container, typename Iterator = typename Container::iterator>
range<exposed_iterator<Iterator>> expose(Container &container)
{
    return { container.begin(), container.end() };
}

Then use all this machinery as follows:

std::list<int> l = { 1, 2, 3, 5};
for (auto &&it : expose(l)) {
    if (*it > 3) {
        l.insert(it, 4);
        break;
    }
}
r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • no0x: Thanks, I guess if there is no ```std::``` way of doing it I better stick with the ```for(i=l.begin();i!=l.end();it++)``` way. – Konrad Eisele Jul 15 '18 at 07:44
0

You can indeed do this, for instance by creating an extra variable that holds the position you are currently at, or using std::find like this:

int i = 0;
for (auto& iter : l) {
    if (i == 3) { /* do something */; break; }
    //...
    ++i;
}


auto iter = l.find( /* find condition */ )
if (iter == l.end()) {
    // handle not-found case
}
else {
    // do something
}

However, this sort of defeats the purpose of a range-based for loop, which is not needing an index, and as such you would be better off using a regular for-loop instead.

L. Kue
  • 473
  • 5
  • 19
  • In the first piece of code, `i` is an *index*, not an *iterator*, and can't be used with `insert`. Also, `iter` is the value in the list, which doesn't seem like a good naming convention, and you check whether the index is 3 instead of whether the value in the list is 3, which is not what the sample question does. – Daniel H Jul 14 '18 at 21:13
  • `l` is a `std::list` and has no member named `find`. – aschepler Jul 14 '18 at 22:02