1

I have stumbled across this piece of code and trying to understand it, certain section are clear but not for loop:

forward_list<Class> obj;
auto b_end = obj.before_begin();

for (auto& _ : obj)
    ++b_end ;

obj.insert_after(b_end, Class newObj);

Not exactly sure what for loop is doing. Could some break the for loop down and help me understand it?

Edit: This question is marked duplicate but other threads do not provide sufficient information on it and/or are too old.

Jinesh
  • 95
  • 6
  • 1
    It's called [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for) – rustyx Oct 30 '17 at 20:06
  • I won't be able to post the full code, but from what I can tell the objective of the code is to insert newObj always at the end of the list(technically not at the end but one before end since it might point to nullptr??) – Jinesh Oct 30 '17 at 20:14
  • 2
    @Jin: A [mcve] is not the full code. It is about creating a toy example and reproducing the behavior you see. A very useful exercise for debugging and understanding. – AndyG Oct 30 '17 at 20:15
  • I think `auto b_end = obj.before_begin() + std::distance(std::begin(obj), std::end(obj));` would work and eliminate the hand-rolled loop. Am I correct? – Fred Larson Oct 30 '17 at 20:22
  • @FredLarson Yes that would work, but you'd iterate through the list twice just to get the iterator to the last element. – Corristo Oct 30 '17 at 20:25
  • Just sayin' but `_` is one of the worst variable names I've ever seen. – user4581301 Oct 30 '17 at 20:29
  • @FredLarson But you still need one iteration to calculate `std::distance` and then one iteration to move from `before_begin` to the last element. – Corristo Oct 30 '17 at 20:32
  • @Corristo: Yeah, you're right. Also, no `+` operator, so you have to use `std::next` instead. What a pain this container is. – Fred Larson Oct 30 '17 at 20:32

2 Answers2

1

This:

forward_list<Class> obj;

declares a std::forward_list of Class-es named obj. The std:: part is omitted due to use of using namespace std; somewhere in the code. This:

auto b_end = obj.before_begin();

defines an iterator pointing at the std::forward_list::before_begin location in your obj list. This:

for (auto& _ : obj)
    ++b_end;

is a range based for loop that uses an oddly named _ variable passed by reference. In this loop the b_end gets incremented in each iteration. Finally:

obj.insert_after(b_end, Class newObj);

tries to insert a new newObj of type Class after the b_end position and fails to compile because it should probably be:

Class newObj;
obj.insert_after(b_end, newObj);

That being said, the _ is just a variable name. It is a (somewhat unusual yet) valid variable identifier. In here the _ variable itself is not used in any way in the for loop so the whole thing could have been rewritten as:

for (auto el : obj){
    ++b_end;
}

The auto keyword stands for an auto specifier.

Ron
  • 14,674
  • 4
  • 34
  • 47
1

std::forward_list is a sequential container with forward_iterators. It is a one-sided singly-linked list. To insert a value into the list you can use only the method insert_after specifying an iterator in the range [begin(), end()) or the iterator returned by the method before_begin().

From the C++ Standard

5 Requires: position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).

So if you are going to append a new value to the end of a list you have to move an iterator in the position that corresponds to the last element in the list.

So this loop named range-based for loop

for (auto& _ : obj)
    ++b_end ;

moves step by step the iterator b_end in the position occupied by the last element in the list. So now using the iterator you can append a new value to the list using the method insert_after.

Consider a simple demonstrative program.

#include <iostream>
#include <forward_list>

int main()
{
    std::forward_list<int> lst = { 1, 2 };

    auto b_end = lst.before_begin();

    for (const auto &_ : lst)
    {
        ++b_end;
        std::cout << *b_end << '\n';
    }
    std::cout << std::endl;

    lst.insert_after(b_end, 3);

    for (const auto &_ : lst) std::cout << _ << ' ';
    std::cout << std::endl;

    return 0;
}

The program output is

1
2

1 2 3

At first the iterator b_end points to before the first element in the list.

    auto b_end = lst.before_begin();
                     ^^^^^^^^^^^^^^

After that in the loop that will have two iterations because the list contains only two elements the iterator at first will be moved in the position that corresponds to the first element and then in the position that corresponds to the second element.

Now using the obtained iterator we can add the value 3 after the value 2.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335