使用成员std::tuple来迭代对象的成员向量。

4

我正在尝试遍历一个对象的成员std::vector,使用一个包含成员向量引用的std::tuple成员。

一个简单的例子可以像这样:

#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);
}

这个可以工作,但问题在于,如果我将对象 s 分配给 std 容器,则 tup 持有的引用可能会成为悬空引用,例如:
    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.

有没有什么好的方法来解决这个问题?我希望引用指向新复制的成员,而不是原始成员,以便可以使用成员元组迭代新对象的成员向量。

(在问了this问题之后,写下了这个问题。)


4
不要有一个被初始化并且复制的数据成员tup,而是要有一个成员函数tup(),它返回带有引用的元组。你可以方便地使用std::tie()来创建它。 - j6t
一些要点是:1)使用成员函数而不是成员数据,这样就没有数据副本,因此也没有悬空引用。2)返回std::tie很方便,它等同于返回元组tuple<vector<int>&,...>tup{a,b,...}。3)参见这个答案,了解std::tiestd::forward_as_tuple之间的差异的好解释。 - starriet
1个回答

4

关键问题在于无法重新分配引用。因此,在复制/移动 S 时,您不能使元组内部的引用指向新对象内部的引用。

最简单的方法是只创建一个成员函数而不具有数据成员:

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

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

如果你真的想将其作为成员数据,你可以使用原始指针。拥有非所有权的原始指针完全没问题。这样,你就可以编写自定义的复制/移动操作来修复指针:

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); }
};

如果你真的希望它们作为可用性参考而可访问,你可以将tup设为私有成员,并编写一个成员函数来返回一个引用的元组。

2
自定义复制构造函数不需要重新分配内存。S(const S& other) : a(other.a), b(other.b), tup(a,b){}。赋值运算符也不需要重新分配内存。赋值后,ab仍然是同一成员。(尽管如此,使用成员函数仍然是个更好的主意。)根本原因是默认的复制构造函数会从源中复制引用,而我们不希望出现这种情况。 - Raymond Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接