30

I want to use a circular list.

Short of implementing my own (like this person did) what are my options?

Specifically what I want to do is iterate over a list of objects. When my iterator reaches the end of the list, it should automatically return to the beginning. (Yes, I realize this could be dangerous.)

See Vladimir's definition of a circular_iterator: "A circular_iterator will never be equal with CircularList::end(), thus you can always dereference this iterator."

Runcible
  • 7,006
  • 12
  • 42
  • 62

5 Answers5

37

There's no standard circular list.

However, there is a circular buffer in Boost, which might be helpful.

If you don't need anything fancy, you might consider just using a vector and accessing the elements with an index. You can just mod your index with the size of the vector to achieve much the same thing as a circular list.

Naaff
  • 9,213
  • 3
  • 38
  • 43
  • 8
    Thanks Naaff! Modding the index with the size of the vector is such a simple solution, I'm embarrassed I didn't think of it. – Runcible Jun 03 '09 at 22:13
  • 2
    If you ensure that the size of your `vector` is a power of two, then instead of the expensive overhead of the modulus operation, use the bitwise `&` operator instead as it only costs one cycle. It works like this: `(n mod (2^k)) == (n & (2^k - 1))` e.g. `n % 256 == (n & (255))` – Benjamin R Mar 20 '17 at 06:27
  • 2
    The compiler will replace mod with bit operations where appropriate for you anyway, no need to obfuscate code unless you really need the unoptimized debug performance – GeckoGeorge Dec 23 '20 at 09:31
21

If you want something looking like an iterator you can roll your own, looking something like

template <class baseIter>
class circularIterator {
    private:
        baseIter cur;
        baseIter begin;
        baseIter end;
    public:
        circularIterator(baseIter b, baseIter e, baseIter c=b)
            :cur(i), begin(b), end(e) {}
        baseIter & operator ++(void) {++cur; if(cur == end) {cur = begin;}}
};

(Other iterator operations left as exercise to reader).

Captain Segfault
  • 1,686
  • 8
  • 11
6
list<int>::iterator circularNext(list<int> &l, list<int>::iterator &it)
{
    return std::next(it) == l.end() ? l.begin() : std::next(it);
}
Mahmoud
  • 9,729
  • 1
  • 36
  • 47
4

In addition to @captain-segfault and @mahmoud-khaled's iterator-focused answers, you can also use std::list as a circular list by altering what you do to retrieve elements from it. Use splice to move one end of the list to the other end as you process it.

template <typename T>
T & circularFront(std::list<T> & l)
{
  l.splice(l.end(), l, l.begin());
  return l.back();
}
template <typename T>
T & circularBack(std::list<T> & l)
{
  l.splice(l.begin(), l, l.rbegin());
  return l.front();
}
fuzzyTew
  • 3,511
  • 29
  • 24
0

I found this solition. Works fine for me.

std::list<int> List{ 1,2,3,4,5,6 };

    auto it = List.end();

    it--;

    it._Ptr->_Next = List.begin()._Ptr; // Next Node of the last elemen is now first elemen of the List
    List.begin()._Ptr->_Prev = it._Ptr; // Prev Node of the first element is now Last node

    for (int num : List)
    {
        std::cout << num << '\n';
    }

In this case we will loop indefinitely. Should work backward too.

Output

1
2
3
4
5
6
1
2
3
4
5
6
1
.
.
  • Hi Doctor smail, the question here is not about making a linked list circular, Runcible is looking for a *list class* that is circular. – Goodies Aug 20 '21 at 23:52