【问题标题】:vector<reference_wrapper> .. things going out of scope? how does it work?vector<reference_wrapper> .. 事情超出范围?它是如何工作的?
【发布时间】:2026-02-03 13:20:05
【问题描述】:

用例:我正在将数据从我的一个非常旧的程序转换为数据库友好格式。有些部分我必须对旧数据进行多次传递,因为特别是在我可以在关系中引用它们之前,键必须首先存在。所以我想为什么不在第一次通过时将不完整的部分放在引用向量中并从工作函数中返回,这样我就可以轻松地使用该向量对仍然不完整的部分进行第二次传递。我喜欢尽可能避免使用指针,所以我查看了std::reference_wrapper&lt;T&gt;,这似乎正是我所需要的......除了我根本不明白它的行为。

我有 vector&lt;OldData&gt; old_datavector&lt;NewData&gt; new_data 作为我的转换类的成员。转换成员函数本质上是:

//...
vector<reference_wrapper<NewData>> incomplete;
for(const auto& old_elem : old_data) {
    auto& new_ref = *new_data.insert(new_data.end(), convert(old_elem));
    if(is_incomplete(new_ref)) incomplete.push_back(ref(new_ref));
}
return incomplete;

但是,incompletefor 循环之后立即被破坏。该程序编译,但崩溃并产生乱码。现在我不知道我是否正确放置了ref,但这只是我尝试将其放在其他地方的众多尝试之一,请改用push_backemplace_back,等等。.. 似乎有些事情超出了范围,但是什么? new_dataold_data 都是类成员,incomplete 也在循环之外,根据文档,reference_wrapper 是可复制的。

这是一个简化的 MWE,它会编译、崩溃并产生乱码:

// includes ..
using namespace std;
int main() {
    int N = 2; // works correctly for N = 1 without any other changes ... ???
    vector<string> strs;
    vector<reference_wrapper<string>> refs;
    for(int i = 0; i < N; ++i) {
        string& sref = ref(strs.emplace_back("a"));
        refs.push_back(sref);
    }
    for (const auto& r : refs) cout << r.get(); // crash & gibberish
}

这是g++ 10.2.0-std=c++17,如果这意味着什么。现在我可能只使用指针并完成,但我想了解这里发生了什么,文档/搜索似乎没有帮助..

【问题讨论】:

  • *new.insert ? new 是一个关键字,所以这不是真正的代码。为什么要在末尾插入而不是 push_back
  • Godbolt 现在有消毒剂godbolt.org/z/nG4snz
  • 你的问题是向量迭代器失效。 godbolt.org/z/nzoMKK
  • @RyanHaining 哦,当然,对不起,我在这里输入了这个片段,没有检查正确的变量名。会编辑。在那次尝试中,我使用 insert 来获取我需要存储在某处的引用。我也尝试了 push_back,然后使用 .back() 获取插入的元素,但发生的事情几乎相同
  • 您的简化代码会出现不同的问题,这就是 N=1 有效的原因。每当您添加到向量时,它可能必须增长。如果它增长,它实际上是在分配一个新的内存块并将所有元素从旧块移动到新块。在您的示例中,当您插入第二个元素时,向量的容量增加到 2,并且指向第一个元素的引用/指针现在无效。

标签: c++17 reference-wrapper


【解决方案1】:

这里的问题是您正在使用vector 数据结构,它可能会在您添加元素时为整个向量重新分配内存,因此该向量上的所有先前引用很可能会失效,您可以解决您的问题使用list 而不是vector 的问题。

【讨论】: