This question already has answers here:
Range based loop: get item by value or reference to const?
(5 answers)
Closed 4 years ago.
Suppose a class Object exists.
I now have vector<Object> vec
If I now do in some part of my code:
for(auto p : vec){
//something with p
}
Does this make a new object p for every object that actually in the iterator? Or does it actually just go through the actual objects in the list? I'm asking because I've also seen auto& p
Yes, it will make a copy of each item, to have a reference you should do:
for (auto& p : vec) {}
Also, if you want to protect items from any change, you can have const to reference:
for (const auto& p : vec) {}
The range based for loop you have
for(auto p : vec) /* ... */
copies every element in vec into the loop variable p. Hence, any changes you apply to p does not affect the elements in vec. And yes, if you change the loop to
for (auto& p : vec) /* ... */
then p is a reference to the vec elements, and changes applied to p are changes you apply to the corresponding vec element.
Related
The following code will not alter the contents of i in the for loop:
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if (target == 0) {return vector<vector<int>>{{}};}
else if (!candidates.size() || target < 0) {return vector<vector<int>>();}
else {
vector<vector<int>> with = combinationSum(candidates, target - candidates[0]);
vector<int> new_vector(candidates.begin() + 1, candidates.end());
vector<vector<int>> without = combinationSum(new_vector, target);
for (auto i : with) {i.push_back(candidates[0]);}
with.insert(with.end(), without.begin(), without.end());
return with;
}
}
};
However, if I change it to auto& i : with ..., it works. Is there a reason why? I thought references were only relevant if you are working with pointed objects or if you don't want the variable (in this case i) to change in the local scope.
auto defaults to being neither const nor a reference. So for (auto i : with) { is storing to i by value, which invokes the copy constructor for each of the nested vectors. That copy has nothing to do with the version stored in your vector of vectors, so no changes persist when the copy goes away. Your addition of & makes it a reference that aliases the underlying inner vector so changes to the reference change the original vector.
If you don't want to change the object in question, the efficient way to do this is to use const auto& to both prevent changes and avoid copies; it's fairly rare you want to iterate by value this way unless you really want a mutable copy that won't affect the original, since the copy constructor costs can get pretty nasty.
On using for (auto i : with), you are copying the element i from the vector with and then using it.
On using for (auto & i : with), you are referencing the element i from the vector with and then using it.
Your vector would change if you were copying the addresses of its elements then dereferencing (or you were using a vector of pointers) but here you are copying the objects.
for (auto i : with) { ... is equivalent to:
for (vector<int>::iterator it = with.begin(); it != i.end(); it++)
{
vector<int> i = *it;
...
And you can then see that i makes a copy each time round the loop. Changes to this copy therefore affect only the copy, which is a temporary.
OTOH for (auto &i : with) ... is equivalent to:
for (vector<int>::iterator it = with.begin(); it != i.end(); it++)
{
vector<int>& i = *it;
...
which assigns a reference each time round the loop. Changes to this reference therefore affect the original object.
This question already has answers here:
Range based loop: get item by value or reference to const?
(5 answers)
C++ range for loop different than regular for loop [closed]
(1 answer)
Closed 3 years ago.
I have a vector that contains Enemy objects called enemiesOnField. When calling a function takeYourTurn that potentially changes the coordinates of individual enemies I noticed that even though the change occurs within the function, it doesn't persist in the enemiesOnField vector. This leads me to believe that when calling this function by iterator it is somehow called on a copy, not the original object. When calling by reference returned by the [] operator everything works normally.
The code that doesn't work as expected is commented out:
void World::letNPCsAct()
{
/* this doesn't work
for (auto it : enemiesOnField)
it.takeYourTurn();*/
// this works
for (int i = 0; i < enemiesOnField.size(); ++i)
enemiesOnField[i].takeYourTurn();
}
Why is this like this??
Normal for loop works because you are directly changing the content. Range based for loop creates copies by default. So your content won't be modified. If you want to modify, you need to use the reference as below.
for (auto& it: enemiesOnField) {
it.takeYourTurn();
}
The range-for loop gives you the objects contained in the range, not iterators to them.
for (auto it : enemiesOnField)
it.takeYourTurn();
This is not calling takeYourTurn through an iterator. It is calling it on a copy of the object contained in enemiesOnField.
If you want it to use references, you must declare it to do so:
for (auto& enemy : enemiesOnField)
enemy.takeYourTurn();
The range-for you used is semantically equivalent to the following:
{
auto&& __range = enemiesOnField;
auto __begin = __range.begin();
auto __end = __range.end();
for (; __begin != __end; ++__begin)
{
auto it = *__begin;
it.takeYourTurn();
}
}
(Note that the names __range, __begin, and __end are for exposition only. Those objects cannot be referenced by user code)
Note that auto it = *__begin makes a copy of the object referenced by __begin.
Try this:
for (auto& it: enemiesOnField) {
it.takeYourTurn();
}
You need to iterate through references.
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.
This question already has answers here:
How can I modify values in a map using range based for loop?
(4 answers)
Closed 5 years ago.
pair<bool, char> pos_panlindrome(unordered_map<char, int>& smap){
for(auto m : smap){
m.second /= 2;
}
}
Where I declared smap in another function calling pos_panlindrome. I want all the mapped value (in spite of the key value) to be halved. But somehow, when I print out the smap after pos_panlindrome function, none of the mapped_value got changed.
Why is that case?
The problem is that you're using an unmodified auto in your for loop. This creates a copy of each map element, and then you modify the copy. You need a reference if you want to change the entry in the map. Try this instead:
for (auto& m : smap)
{
m.second /= 2;
}
This question already has answers here:
How to use range-based for() loop with std::map?
(5 answers)
Closed 7 years ago.
I know that in order to loop through a map in C++11 I can use the following code:
std::mymap<std::string, myclass>
for(auto item : mymap)
{
// code here
}
What exactly is referred to by item? Is it the map key? The value that is currently accessed? If I want to do something with the value, how do I access it?
Iter refers to a std::pair<std::string, myclass> in your context. So iter.first is your key and iter.second is value for your example.
If you want to modify value inside loop block, you can write as follows:
iter.second=<new value>
If you don't need to modify the value better to use your range loop as follows:
for(const auto& iter : mymap)
{
//
}