调整大小后保持对矢量元素的引用有效

Posted

技术标签:

【中文标题】调整大小后保持对矢量元素的引用有效【英文标题】:Keeping a reference to a vector element valid after resizing 【发布时间】:2018-06-15 00:32:13 【问题描述】:

如果我们对向量元素进行引用,然后调整向量的大小,则该引用不再有效,迭代器也是如此:

std::vector<int> vec0, 1, 2, 3, 4, 5;
int& ref = vec[0];
auto itr = vec.begin();

cout <<  ref << " " << *itr << endl;

vec[0] = 7;

cout <<  ref << " " << *itr << endl;

vec.resize(100);
vec[0] = 3;

cout <<  ref << " " << *itr << endl;

打印出来:

0 0
7 7
0 0 // We expected a 3 here

而且我知道只保留对向量本身的引用并调用 vec[0] 会更实用,但只是为了提问,是否可以保留一个始终为 vec[0] 的对象] 即使对象被移动?

我已经尝试编写一个小的帮助类来帮助解决这个问题,但我不确定这是否是最好的方法,或者它是否会失败?

template<typename T>
struct HelperClass

    std::vector<T>& vec;
    size_t element;

    HelperClass(std::vector<T>& vec_, size_t element_) : vec(vec_) , element(element_) 

    // Either define an implicit conversion from HelperClass to T
    // or a 'dereference' operator that returns vec[0]
    operator T&()  return vec.at(element); 
    T& operator*()  return vec.at(element); 
;

并通过隐式转换为 T& 或“取消引用”运算符来使用它:

std::vector<int> vec0, 1, 2, 3, 4, 5;
int& ref = vec[0];
auto itr = vec.begin();
HelperClass<int> hlp = HelperClass<int>(vec, 0); // HelperClass

cout <<  ref << " " << *itr << " " << hlp << " " << *hlp << endl;

vec[0] = 7;

cout <<  ref << " " << *itr << " " << hlp << " " << *hlp << endl;

vec.resize(100);
vec[0] = 3;

cout <<  ref << " " << *itr << " " << hlp << " " << *hlp << endl;

已经打印了例外的内容:

0 0 0 0 
7 7 7 7
0 0 3 3

那么除了有一个辅助类之外还有更好的方法吗?辅助类在某些情况下是否不可靠?

我在reddit上也遇到过this的帖子,但是好像他们没有讨论那里的助手类

【问题讨论】:

引用是使用指针实现的。任何在内存中重新定位数据的操作都会使其无效,并且调整数组的大小可能需要重新分配。 @Barmar,是的,我只是问是否可以保留一个对象作为参考,当内存重新分配时不会失效,例如这个帮助类,我也有可能标题用错了,但我不确定什么是更好的标题。 使用方法也可以参考std::reference_wrapper。 看看std::reference_wrapperstd::ref()。与传统参考不同,std::reference_wrapper 可以反弹。因此,您可以获取对所需 vector 元素的引用,并在调整矢量大小后,再次获取对同一元素的新引用。 您的助手“工作”只是因为它通过索引而不是引用访问vector。每次访问帮助程序时,它都会重新评估给定索引处的当前元素,只要该索引在调整大小后没有超出范围,就可以了。 STL 中的任何内容都不会为您做同样的事情,所以是的,您需要编写自己的助手来做您想做的事情。 【参考方案1】:

您可以做的一件事是拥有一个指针向量而不是实例向量。这当然有它自己的问题,但如果你必须让对象引用存活下来,那么矢量调整大小就可以了。

【讨论】:

【参考方案2】:

向量的任何重新分配都会使任何指针、引用和迭代器无效。

在您的示例中,您的 HelperClass 在某种意义上是无用的:

cout <<  ref << " " << *itr << " " << hlp << " " << *hlp << endl;

等同于:

cout <<  ref << " " << *itr << " " << vec[0] << " " << vec[0] << endl;

如果发生重新分配,只需使用迭代器接口 .begin() .end() 再次访问迭代器。

【讨论】:

@FilipeRodrigues:如果函数只得到一个引用,而不能访问vector...谁调整大小它?因为如果该函数可以调用其他可以访问vector 的函数,为什么它不能访问vector 本身? @FilipeRodrigues 你不能那样做。即使您持有指向向量中单个元素的指针,向量的任何重新分配都会使该指针无效。 @FilipeRodrigues 你不能那样做,矢量不是thread safe @FilipeRodrigues: "如果程序是多线程的,例如,当一个函数将项目添加到向量时,另一个函数使用该函数的项目。" 那么你需要有某种mutex,因为两个不同的线程不能同时访问同一个vector。因此,当您调用该函数并向其传递引用时,vectormutex 应保持锁定状态,从而防止其他线程调整其大小。 @FilipeRodrigues:仅供参考:Reddit 的助手类?它对多线程情况没有帮助,因为当助手类获取引用时,它可能已经失效。这就是为什么你必须锁定对同一个vector 的多线程访问。或任何容器,就此而言。

以上是关于调整大小后保持对矢量元素的引用有效的主要内容,如果未能解决你的问题,请参考以下文章

矢量调整大小 - 检测的便携方式

矢量调整大小后下标超出范围

WPF:如何在调整大小后保持 ListBox SelectedItem 可见?

根据寡妇大小调整内容 div 的大小,其余元素将保持不变

类中的 2D 矢量调整大小。在此范围内未声明错误[关闭]

调整大小后通过鼠标连续调整其余图像的大小,并保持比例