How to iterate over non-const variables in C++?
It does not work because in {a,b}
you are making a copy of a
and b
. One possible solution would be to make the loop variable a pointer, taking the addresses of a
and b
:
#include <initializer_list>
struct Obj {
int i;
};
Obj a, b;
int main() {
for(auto obj : {&a, &b}) {
obj->i = 123;
}
}
See it live
Note: it is generically better to use auto
, as it could avoid silent implicit conversions
The reason why that doesn't work is that the underlying elements of the std::initializer_list
are copied from a
and b
, and are of type const Obj
, so you are essentially trying to bind a constant value to a mutable reference.
One could try to fix this by using:
for (auto obj : {a, b}) {
obj.i = 123;
}
but then would soon notice that the actual values of i
in the objects a
and b
didn't change. The reason is that when using auto
here, the type of the loop variable obj
will become Obj
, so then you're just looping over copies of a
and b
.
The actual way this should be fixed is that you can use std::ref
(defined in the <functional>
header), to make the items in the initializer list be of type std::reference_wrapper<Obj>
. That is implictly convertible to Obj&
, so you can keep that as the type of the loop variable:
#include <functional>
#include <initializer_list>
#include <iostream>
struct Obj {
int i;
};
Obj a, b;
int main()
{
for (Obj& obj : {std::ref(a), std::ref(b)}) {
obj.i = 123;
}
std::cout << a.i << '\n';
std::cout << b.i << '\n';
}
Output:
123 123
An alternative way to do the above would be to make the loop use const auto&
and std::reference_wrapper<T>::get
. We can use a constant reference here, because the reference_wrapper
doesn't get altered, just the value it wraps does:
for (const auto& obj : {std::ref(a), std::ref(b)}) {
obj.get().i = 123;
}
but I think that, because using auto
here forces the use of .get()
, this is quite cumbersome and the former method is the preferable way to solve this.
It might seem to be more simple to do this by using raw pointers in the loop as @francesco did in his answer, but I have a habit of avoiding raw pointers as much as possible, and in this case I just believe that using references makes the code clearer and cleaner.