Condensed for loop.. (C++) [duplicate] - c++

This question already has answers here:
std::forward_list and std::forward_list::push_back
(5 answers)
Closed 5 years ago.
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.

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.

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.

Related

What is the difference in using and not using "&" in range-based for loops usually iterating over maps in C++? [duplicate]

This question already has answers here:
Range based loop: get item by value or reference to const?
(5 answers)
Range based for-loop with &
(3 answers)
Closed last month.
for (auto& it: map_name) { // __ some _ code __ }
I want to know whether using & makes any big difference and can we use it to directly access second element of the iterator?
Iterate over maps using structured bindings like this, the references will avoid copying classes.
The const is added because a print loop should never modify
anything in the map.
#include <map>
#include <iostream>
int main()
{
std::map<int, int> map{ {1,2}, {2,5}, {3,7} };
for (const auto& [key, value] : map)
{
// key will be a const reference to the key at the current position in the map
// value will be a const reference to the value at the current position in the map
std::cout << key << ", " << value << "\n";
}
return 0;
}

Construct new forward_list from iterators into existing forward_list via node splicing

If you have a std::list, and want to rebuild it into a new std::list in a different order, without copying, from iterators into the original std::list, it's fairly straightforward, for example, if you're shuffling the iterators and rebuilding from the shuffled order:
int main(int argc, char **argv)
{
std::list<std::string> args(&argv[1], &argv[argc]);
std::vector<std::list<std::string>::const_iterator> vec;
for (auto it = std::cbegin(args), last = std::cend(args); it != last; ++it) {
vec.push_back(it);
}
std::shuffle(std::begin(vec), std::end(vec), std::mt19937(42)); // Shuffle with reproducible PRNG
std::cout << "Shuffled vec:\n";
for (auto it : vec) {
std::cout << *it << std::endl;
}
std::list<std::string> shuffled_args;
for (auto it : vec) {
shuffled_args.splice(std::end(shuffled_args), args, it);
}
std::cout << "Shuffled list:\n";
for (const auto& s : shuffled_args) {
std::cout << s << std::endl;
}
return 0;
}
This works great; on my system, when compiled with g++ -std=c++17 -O3 -flto -Wall shuffle_list.cpp and run with ./a.out a b c d e, the results from printing both the vector and the shuffled list agree (e a c d b in that order): Try it online!
But when I try to write a variant version using std::forward_list, things get dicier. This is the only version that doesn't segfault (with comments indicating changes):
int main(int argc, char **argv)
{
std::forward_list<std::string> args(&argv[1], &argv[argc]);
std::vector<std::forward_list<std::string>::const_iterator> vec;
for (auto it = args.cbefore_begin(), last = std::cend(args); std::next(it) != last; ++it) { // Changed to store iterators before each element since splice_after needs them
vec.push_back(it);
}
std::shuffle(std::begin(vec), std::end(vec), std::mt19937(42));
std::cout << "Shuffled vec:\n";
for (auto it : vec) {
std::cout << *std::next(it) << std::endl; // Must advance each iterator to see value stored
}
std::forward_list<std::string> shuffled_args;
auto splice_loc = shuffled_args.cbefore_begin(); // Must begin inserting at beginning of new forward_list
for (auto it : vec) {
shuffled_args.splice_after(splice_loc, args, it); // splice_loc is before when node should go, it points to node before node to splice, great
splice_loc = it; // it should now be last element, it will be next splice location
}
std::cout << "Shuffled list:\n";
for (const auto& s : shuffled_args) {
std::cout << s << std::endl;
}
return 0;
}
The output here is the same from the vector, but for the resulting forward_list it just outputs e: Try it online! If you try other fun things like replacing splice_loc = it; with ++splice_loc; (which is logically equivalent; you spliced in a node after the current location, and advancing the iterator should move you to that new node), it segfaults.
I think I know why this is broken (it's two different ways for the full code and the replacement with ++splice_loc), and I don't think the approach I'm taking is salvageable. In the segfaulting code, as I splice nodes over, the iterators remain valid, but some of the iterators in the original structure are moved before I get to them (e.g. I use position 1's iterator to move the item at 2, and when I try to move 3 via 2, it moves some other random thing that follows 2 in the new list), and now I'm trying to splice in what follows them in the new forward_list (and violating the API, as I claim they came from args, when they've in fact been moved to shuffled_args at that point). There's no good fix for this in the design I've chosen AFAICT. Update: In the non-segfaulting code, I should be saving off std::next(it) before the splice and assigning that to splice_loc (by assigning it, which is probably still in the original forward_list, the splice_loc now points to the original list and I'm probably engaging in undefined behavior that ultimately modifies the original list more than the new one).
My question is:
Is there a good way to do what I want, taking iterators from a forward_list, jumbling them up (with shuffle or sorting or whatever), then building a new forward_list in an alternate order, by direct node transfer (no copies or moves of any of the contained elements), and doing so efficiently? The best I can come up with is making a new dedicated forward_list for each node in the vector, so I can splice that single node over to the final forward_list. This feels ugly, and possibly more expensive than the list equivalent behavior. Is there a better way? Is the many one-element forward_list solution actually great?
For the curious: This is a stripped down reproducer of a problem I'm having writing a keyed sorting algorithm (as in Schwartzian transform aka decorate-sort-undecorate), entirely as a personal challenge. I'm trying to make a specialized version for std::list and std::forward_list that avoids any copies or moves of the values being sorted by decorating the iterators rather than the values themselves, so once I've sorted the collection by the computed keys, I can then construct a new list or forward_list by using splice/splice_after to rebuild the container in the sorted order, without copying or moving a single one of the contained values.
This is a working version based on my original suggestion to use a bunch of single node std::forward_lists. It feels inefficient to make all this std::forward_lists, but from what I can tell, std::forward_list is spec-ed so narrowly that it's almost guaranteed to be implemented as a wrapper around a single pointer, which should be pretty low overhead. And it does work, with no copies or moves of the contained elements, nor (thanks to the use of deque) any copies or moves of the forward_lists themselves (aside from when we empty them to splice them onto the output forward_list), and it only traverses the input forward_list once (destroying it by extracting the first node over and over until it's emptied).
It's not the prettiest, but it's not nearly as ugly or inefficient as I was expecting.
int main(int argc, char **argv)
{
std::forward_list<std::string> args(&argv[1], &argv[argc]);
std::deque<std::forward_list<std::string>> deq; // Use deque so we never have to move existing forward_lists, and don't need to pre-walk to find size to reserve for vector
while (!args.empty()) {
auto& flist = deq.emplace_back();
flist.splice_after(flist.cbefore_begin(), args, args.cbefore_begin()); // Extract a single node from input to populate new forward_list
}
std::shuffle(std::begin(deq), std::end(deq), std::mt19937(42)); // Shuffle with reproducible PRNG
std::cout << "Shuffled deq:\n";
for (auto& flist : deq) {
std::cout << flist.front() << std::endl;
}
std::forward_list<std::string> shuffled_args;
auto splice_loc = shuffled_args.cbefore_begin();
for (auto&& flist : deq) {
shuffled_args.splice_after(splice_loc, std::move(flist)); // splice single element forward_list contents onto end
++splice_loc; // We added one element, move the iterator forward by one
}
std::cout << "Shuffled list:\n";
for (const auto& s : shuffled_args) {
std::cout << s << std::endl;
}
return 0;
}
Try it online!
To be clear, if anyone has any better solutions, I'm happy to hear about them and would love to give the checkmark to something cleaner.
What you want is not compatible with the concept of a singly-linked list (which is what forward_list is). Your algorithm, at its core, requires being able to use an iterator into a list as sufficient information to extract that node from the list.
To remove a node from such a list, you need to get the previous node and change its "next" pointer to point to the to-be-removed node's "next" pointer. You cannot get the previous node just from a particular node in a singly-linked list.
If you jumble up a bunch of pointers to nodes of a singly-linked list (which is what forward_list::iterators are), there is no way to get the previous node in order to extract that node.
The only thing you could do is to extract all of the nodes into individual forward_lists containing exactly one element, jumble them up, and splice those single-element lists into the final list. But even this requires care, as splice_after does not return an iterator. So you'll need to get the iterator from the single-element list before the splice, splice it in, and then use that as the pos for the next splice. The very first one shouldn't be a splice; it should instead be a move.

How to find unique values in a vector c++

I am trying to solve a coding problem where I am to check and see if a vector has unique values and if it does then return true else false.
So Far I thought of using a nested loops where you would compare the first to the last, but I am wanted to know if C++ has anything else then doing a o(n^2) type iteration. I saw that c++ has a unique function, but that would delete the unique value.
Example 1:
Input: nums = [1,2,3,1]
Output: true
Example 2:
Input: nums = [1,2,3,4]
Output: false
std::unique checks for consecutive duplicates and moves them to the end of the range. It does not remove them from the vector. Anyhow you can make a copy. It also returns an iterator to the end of the range of unique values (that are now in the front of the vector):
#include <iostream>
#include <vector>
#include <algorithm>
bool only_unique(std::vector<int> v) {
std::sort(v.begin(),v.end());
return std::unique(v.begin(),v.end()) == v.end();
}
int main(){
std::cout << only_unique({1,2,3,1});
std::cout << only_unique({1,2,3,4});
}
If you don't want to use the additional memory you can change the argument to a reference. Currently, only_unique leaves the parameter unmodified. Even if the vector is passed by reference the duplicates will still be present (just at a different position). This has O(n log n) complexity.
you need to create "set" structure,
it makes it possible to insert values from vector and does not insert the duplicates,
so you can check if the size of the set and the vector match or not.
set<int> st;
for (auto i : nums)
st.insert(i);
return st.size() == nums.size();

How to use an iterator pointing to a vector of structs c++?

I need help with my code. I don't understand how to use the returned pointers to access elements in a struct stored in a vector :
struct receptionEvents
{
int chFreqIndex; //0
int chSFIndex; //1
simtime_t endReceptionTime ;
double SNR; //2
bool collidedFlag; //0= false(Not Corrupted) 1=Corrupted
};
std::vector<receptionEvents> buffRxEvents;
Now in main function I am trying to find all structs named receptionEvents that match a certain chFreqIndex using the below line :
int chFreqIndexCheck == 4;
auto vpit = find_if( buffRxEvents.begin(), buffRxEvents.end(), [&]
(receptionEvents const & m) { return m.chFreqIndex == chFreqIndexCheck;} );
Now my problem is how to use the vpit iterator to iterate over all found entries in my vector and how to access the data variables of each. Can any one help please?
The std::find family of functions returns an iterator to a single element (or end if nothing is found).
If you want to get all elements that matches your condition, then you could use std::copy_if to copy to a new container (possibly with the help of std::back_inserter).
You can of course have your own loop, calling std::find_if multiple times and pass the previous vpit plus one as the beginning of the range you search through.
Or use std::for_each or a range-based for loop with a check for the current element to see if it matches your condition.
std::find_if only finds the first occurence. But you could search the next element beginning at the successor of the currently found element, such as:
auto vpit = buff.begin();
while(vpit != buff.end())
{
vpit = std::find_if(vpit, buff.end(), [](/*...*/){ /*...*/ });
if(vpit != buff.end())
{
// use it
++vpit; // for not finding current element AGAIN!
}
}
Simpler, though, is a simple range based for loop:
for(auto e : buff)
{
if( /* ... */ )
{ /* ... */ }
}
Edit:
but how exactly can I use it
You can just use it as if it was a pointer: *vpit to access the data the iterator "points to", vpit->x to access a member of, if data is a complex type (as in your example).
Example:
receptionEvents someCopy = *vpit; // copies entire struct
int cfIndex = vpit->chFreqIndex;

Which order is used to stack or heap variable creation [duplicate]

This question already has an answer here:
How do I erase elements from STL containers?
(1 answer)
Closed 8 years ago.
I've this code:
#include <algorithm>
#include <iostream>
#include <list>
using namespace std;
struct P
{
bool operator()(const int &n) const
{
return n % 3 == 0;
}
};
int main()
{
std::list<int> l({ 5, 2, 6, 1, 13, 9, 19 });
std::cout << l.size();
std::remove_if(l.begin(), l.end(), P());
std::cout << l.size() << std::endl;
return 0;
}
prints out "77". I expected it would have printed out "75", because the operator () of the P struct, returns true when its argument has not remainder of the division by 3. And that's the case for '6' and '9' (two elements out of seven).
Am I missing something ?
thanks.
To quote from http://www.cplusplus.com/reference/algorithm/remove_if/
The function cannot alter the properties of the object containing the range of elements (i.e., it cannot alter the size of an array or a container): The removal is done by replacing the elements for which pred returns true by the next element for which it does not, and signaling the new size of the shortened range by returning an iterator to the element that should be considered its new past-the-end element.
In other words, it rearranges the elements in the given range so that all the non-removed ones are at the beginning, then returns an iterator just past the end of the non-removed part. But it can't delete any elements, because it doesn't know anything about the underlying container.
Is it possible std::remove_if returns the resulting list?
remove/remove_if only reorders a sequence, it doesn't modify it. Iterators have no access to or knowledge of the container from which they come. You need to pass the result to a suitable erase container member:
l.erase(std::remove_if(l.begin(), l.end(), P()), l.end());
Don't forget the second l.end() so that you get the two-iterator overload of erase that erases a whole range. If you forget it, you end up with the one-iterator overload that only erases a single element.