0

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 Answers2

0

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.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • I am amazed at how much more code is generated for the provided demo vs the copy-pase approach. https://godbolt.org/z/8GfzxM – Aleksander Bobiński Sep 10 '20 at 12:32
  • @AleksanderBobiński That's why you are supposed to compile with optimizations: https://godbolt.org/z/qhGfG7 – Yksisarvinen Sep 10 '20 at 12:34
  • @AleksanderBobiński You shouldn't measure without optimizations. At `-O2` [both](https://godbolt.org/z/qhGfG7) are basically [equivalent](https://godbolt.org/z/Prcz37). – cigien Sep 10 '20 at 12:35
  • Good catch but can this difference be considered a missed optimization? Both clang and gcc generate more code with the indirect approach. – Aleksander Bobiński Sep 10 '20 at 12:38
  • @AleksanderBobiński A few more lines of assembly *doesn't* automatically imply slower. You'll need to measure the run-time to see if there's any difference. I'm not saying it's more or less efficient, just that you have to measure it to know. – cigien Sep 10 '20 at 12:39
  • I realize this is off-topic here but It does imply more flash memory is required which is far scarier. – Aleksander Bobiński Sep 10 '20 at 12:43
0

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.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52