Accessing references to existing object in a range based for loop with structured binding

145 Views Asked by At

I am struggling to find an easy solution to alter some already existing objects. Lets assume I have the following pairs

std::pair<int, foo> p1 = {1,foo()};
std::pair<int, foo> p2 = {2,foo()};
std::pair<int, foo> p3 = {3,foo()};

with foo being a class with the method alter(). And I would want to alter only the foo-object of each of those pairs. Then I could do so via:

p1.second.alter();
p2.second.alter();
p3.second.alter();

Or, in my opinion a tiny bit less redundant:

for(auto&& p : 
   std::vector<std::reference_wrapper<std::pair<int, foo>>> {p1, p2, p3}) 
{
    auto&& [pi, pfoo] = p.get();
    pfoo.alter();
}

But what I would really like to have would be something like:

for(auto&& [pi, pfoo] : {p1, p2, p3}) 
{
    pfoo.alter();
}

which obviously does not work as pfoo is only a copy then. So is there any way to not copy p1, p2 and p3 in that loop? I am of course aware that one could start off with e.g. a vector holding the three pairs, but is there any other way?

2

There are 2 best solutions below

6
On

You could write a loop that iterates over the addresses of the objects, like this:

for (auto *p : {&p1, &p2, &p3})
    p->second.alter();

Here's a demo.

0
On

You can do some magic with parameter packs:

struct S
{
    int x = 0;
    void alter() {x++;}
};


template<typename... Args>
void foo(std::pair<int, Args>&... args)
{
    ((args.second.alter()),...);
}

int main()
{
    std::pair<int, S> a, b{0, {10}}, c{0, {100}};
    std::cout << "Pre foo(): " << a.second.x << " " << b.second.x << " " << c.second.x << "\n";
    foo(a, b, c);
    std::cout << "Post foo(): " << a.second.x << " " << b.second.x << " " << c.second.x << "\n";
}

// output:
Pre foo(): 0 10 100
Post foo(): 1 11 101

I'm afraid I couldn't get it to work without template as parameter (i.e. to make it only accept S as second type in pair), perhaps this answer will inspire someone to do better.