调整大小后保持对矢量元素的引用有效
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_wrapper
和std::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
。因此,当您调用该函数并向其传递引用时,vector
的 mutex
应保持锁定状态,从而防止其他线程调整其大小。
@FilipeRodrigues:仅供参考:Reddit 的助手类?它对多线程情况没有帮助,因为当助手类获取引用时,它可能已经失效。这就是为什么你必须锁定对同一个vector
的多线程访问。或任何容器,就此而言。以上是关于调整大小后保持对矢量元素的引用有效的主要内容,如果未能解决你的问题,请参考以下文章