Search code examples
c++c++17c++20

Is there a way to specify which parts of a structured binding should be references?


I have a POD struct

struct query{
 int l, r, ans;
};

and I want to write a range-based loop:

std::vector<query> q;

for (auto [l, r, &ans]: q) {
    // perhaps I want to modify l and r here, but I want to store the result in ans.
    // Furthermore, I will reuse Q with the same **original** l and r later, but
    // don't want to create temporary variables to achieve the same effect 
}

There are many obvious quick solutions to this "problem", involving just making an external array for ans, making the temporaries, or maybe hacking something together with std::tie:

        int ty, l, r, x;
        int& ans = elem.ans; 
        std::tie(ty, l, r, x, std::ignore) = std::tie(elem.ty, elem.l, elem.r, elem.x, elem.ans);

so yes, I'm grieving over 2 lines of extra code. But any nicety of c++17 and c++20 features I can abuse to write clearer and shorter code, I want to know.

For the record, code snippet 1 doesn't compile; as per cppreference, it has to be an identifier, without any ref-qualifiers. So I don't think structured bindings will work here. But maybe some std::tie magic or some other language feature I'm not aware of will solve my issue? Any ideas appreciated.


Solution

  • Well, you can always create structured bindings to another tuple...

    query q {1, 2, 0};
    auto [l, r, ans] = std::make_tuple(q.l, q.r, std::ref(q.ans));
    ans = l + r;
    std::cout << q.ans; // 3
    

    std::make_tuple will create a tuple holding values that are copied/moved from each corresponding argument, unless that argument is a std::reference_wrapper. In that case, the tuple will refer to whatever the reference wrapper referred to. std::reference_wrapper is used in a lot of places in the standard library to explicitly require reference semantics.

    Now, if you are willing to pass your vector through a transform view that does the above to every member, you can kinda get what you want

    auto make_binding = std::views::transform([](query& o) { return std::make_tuple(o.l, o.r, std::ref(o.ans)); });
    for (auto [l, r, ans]: q | make_binding) {
        
    }