如何解决对象复制后成员std::tuple持有的vector引用悬空问题?
解决方法
方案1:动态生成引用tuple(推荐)
不在结构体中存储引用tuple,而是提供成员函数在需要遍历的时候,动态生成绑定到当前对象成员的tuple。这种方式从根源上避免了引用悬空问题,因为每次使用的都是当前对象的实时引用。
#include <iostream> #include <vector> #include <tuple> #include <map> struct S { std::vector<int> a; std::vector<double> b; // 动态生成绑定到当前对象成员的引用tuple auto get_tuple() { return std::tie(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.get_tuple()); std::cout << "\n"; std::map<int, S> myMap; myMap[3] = s; // 修改原对象的成员,验证新对象的引用不受影响 s.a.push_back(4); // 遍历容器中对象的成员 std::apply([&](auto&... v){ ((std::cout << v.size() << " "), ...); }, myMap.at(3).get_tuple()); }
输出结果:
2 2 2 2
方案2:自定义复制/移动控制函数
如果必须在结构体中存储引用tuple,需要手动编写复制构造函数、复制赋值运算符、移动构造函数和移动赋值运算符,确保复制/移动后的对象,其tuple引用绑定到自身的成员,而非原对象的成员。
#include <iostream> #include <vector> #include <tuple> #include <map> struct S { std::vector<int> a; std::vector<double> b; std::tuple<std::vector<int>&, std::vector<double>&> tup{a, b}; // 复制构造:复制成员后,将tuple绑定到当前对象的a和b S(const S& other) : a(other.a), b(other.b), tup{a, b} {} // 复制赋值:先复制成员,再重新绑定tuple S& operator=(const S& other) { if (this != &other) { a = other.a; b = other.b; tup = std::tie(a, b); } return *this; } // 移动构造:移动成员后,绑定tuple到当前对象的a和b S(S&& other) noexcept : a(std::move(other.a)), b(std::move(other.b)), tup{a, b} {} // 移动赋值:移动成员后,重新绑定tuple S& operator=(S&& other) noexcept { if (this != &other) { a = std::move(other.a); b = std::move(other.b); tup = std::tie(a, b); } return *this; } // 保留默认构造函数 S() = default; }; int main() { S s; s.a = {2, 3}; s.b = {5.1, 6.1}; std::map<int, S> myMap; myMap[3] = s; // 修改原对象的a s.a.push_back(4); // 获取容器中对象的tuple引用,验证指向自身的a auto& v = std::get<0>(myMap.at(3).tup); std::cout << v.size() << "\n"; // 输出2,而非3 }
方案对比
- 方案1:代码简洁,无需维护复制控制逻辑,无引用悬空风险,是最优选择。唯一的“代价”是每次遍历需要调用
get_tuple(),但这个操作的开销可以忽略不计。 - 方案2:保持了存储tuple的结构,无需修改原有遍历逻辑,但需要手动维护复制/移动函数,后续如果新增成员变量,需要同步更新tuple和复制控制代码,维护成本较高。
内容的提问来源于stack exchange,提问作者starriet 차주녕
相关产品推荐
相关产品推荐

