Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a member std::tuple to iterate through an object's member vectors

Tags:

c++

c++11

I'm trying to iterate through an object's member std::vectors, using a member std::tuple that contains the references of the member vectors.

A simple example would be like this:

#include <iostream>
#include <vector>
#include <tuple>
struct S {
    std::vector<int> a;
    std::vector<double> b;
    std::tuple<std::vector<int>&, std::vector<double>&> tup{a, b};
};
int main() {
    S s; 
    s.a = {2, 3};
    s.b = {5.1, 6.1};
    std::apply([&](auto&... v){
        ((std::cout << v.size()), ...);
    }, s.tup);
}

This works, but the problem is that if I assign the object s to an std container, the references tup is holding can be dangling references. Such as:

    std::map<int, S> myMap;
    myMap[3] = s; // s.a and s.b has been copied to different addresses.
    auto& v = std::get<0>(myMap.at(3).tup); // -> still refers to the previous s.a, not the copied one.

Is there any decent way to solve this problem? I want the references to refer to the newly copied members, not the original ones so that the member vectors of the new object can be iterated through using the member tuple.

(This question is being written after asking this question.)

like image 954
starriet Avatar asked Oct 28 '25 22:10

starriet


1 Answers

The key problem is that references cannot be re-seated. So when you copy/move an S, you cannot make the references inside the tuple point at the ones inside the new object.

The easiest way is to just make a member function and not have a data member:

struct S {
    std::vector<int> a;
    std::vector<double> b;

    auto tup() {
        return std::tie(a,b);
    }
};

If you really want to have it as member data, you can instead use raw pointers. Having non-owning raw pointers is totally fine. This way, you can write custom copy/move operations which fix up the pointers:

struct S {
    std::vector<int> a;
    std::vector<double> b;
    std::tuple<std::vector<int>*, std::vector<double>*> tup{&a, &b};

    S() = default;
    S(const S& other) : a(other.a), b(other.b), tup(&a, &b) {}
    S(S&& other) : a(std::move(other.a)), b(std::move(other.b)), tup(&a, &b) {}
    S& operator=(const S& other) { a = other.a; b = other.b; tup = std::make_tuple(&a, &b); }
    S& operator=(S&& other) { a = std::move(other.a); b = std::move(other.b); tup = std::make_tuple(&a, &b); }
};

If you really want them to be accessible as references for usability purposes, you could make tup a private member and write a member function which returns a tuple of references.

like image 200
TartanLlama Avatar answered Oct 30 '25 15:10

TartanLlama



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!