【问题标题】:Using for auto in nested loops在嵌套循环中使用 for auto
【发布时间】:2021-12-08 02:34:37
【问题描述】:

美好的一天,我最近一直在尝试迁移到 c++ 以加速我的一些应用程序。可能是由于我的背景缺乏,我犯了一个如此愚蠢的错误,但是,我在尝试使用嵌套循环运行它时得到interrupted by signal 11: SIGSEGV

mongocxx::cursor cursor = coll.find({});
for(auto doc : cursor) {
    std::cout << "TEST FIRST" << bsoncxx::to_json(doc) << "\n";
    for (auto doc2 : cursor) {
        std::cout << "FIRST: " << bsoncxx::to_json(doc) << "\n";
        std::cout << "SECOND: " << bsoncxx::to_json(doc2) << "\n";
    } }

我到底做错了什么,为什么?

【问题讨论】:

  • 您发布的代码缺少"。它无法编译,不是导致 SIGSEGV 的代码。请发帖minimal reproducible example
  • 我对@9​​87654325@ 了解不多,但看起来您可以使用const&amp; 而不是复制:for(auto const&amp; doc : cursor)for (auto const&amp; doc2 : cursor) - 等一下 - 你循环使用相同的@ 987654329@?这大概就是问题所在。内循环不应该结束doc吗? for (auto const&amp; doc2 : doc)
  • 您是在第一个还是第二个 cout 中获得 SIGSEGV
  • @463035818_is_not_a_number 我已经编辑过了,虽然这只是代码示例中的一个错字。如果我忘记添加“,编译器会给我一个不同的错误。无论如何,谢谢你的注意
  • @Anna-Lischen 我希望我能更多地了解mongcxx 以提供正确的答案。我对使用 auto 的引用 (&amp;) 的建议是,然后您将引用 cursor 迭代器指向的对象,而不是 复制 该对象(这就是 auto没有&amp; 就可以了)。关于cursor::begin() 的有趣说明:“如果多次调用begin(),则返回的cursor::iterator 指向下一个剩余结果,而不是最初调用begin() 的结果。 "

标签: c++ nested-loops c++20 mongo-cxx-driver


【解决方案1】:

您的代码中的问题是您在两个循环中都在迭代 cursor,但来自 mongocxx 文档:

[...] 取消引用任何非 end() 迭代器总是给出光标中第一个剩余的文档。递增一个非结束迭代器相当于递增它们。

[...] 如果 begin() 被多次调用,则返回的 cursor::iterator 指向下一个剩余结果,而不是最初调用 begin() 的结果。

在 C++ 中,范围循环只是使用迭代器的一种隐藏方式,问题在于(根据上面的引用),当您增加内循环迭代器时,实际上是在增加外循环迭代器。您的代码(大约)相当于:

for (auto it = cursor.begin(); it != cursor.end(); ++it) {
    // there may or may not be a ++it here, that does not change the explanation
    // but the documentation of mongocxx is unclear on this
    for (++it; it != cursor.end(); ++it) { }
}

问题是当内循环到达cursor.end() 时,你在外循环中增加it,然后你再次将它与cursor.end() 进行比较,这可能就是你得到SIGSEV 的原因。

做你想做的唯一方法是要么有两个游标,要么将所有内容存储在一个容器中(例如,std::vector),然后遍历向量(你不会遇到与 @ 相同的问题987654329@)。


关于评论中提到的 const&amp; 的旁注 — 当您使用 for(auto doc: cursor) 时,这相当于(不完全是,但您会明白的):

for (auto it = cursor.begin(); it != cursor.end(); ++i) {
    auto doc = *it; // here, you make a copy of *it, this is expensive
}

auto 替换为const auto&amp; 会更改doc 的声明,并使其成为对迭代器指向的值的引用。

除了我上面提到的其他问题之外,这会给你带来不正确的结果,因为当你在内循环中迭代时,你实际上也在增加外循环迭代器,所以外循环 doc 引用将要么指向不再存在的东西(UB),要么指向新迭代器的底层对象,实际上是doc2,所以你将拥有&amp;doc == &amp;doc2,即docdoc2都指向同一个对象(const auto&amp;)。

【讨论】:

  • @TedLyngmo 你可能是对的。
  • @TedLyngmo 实际上我不确定...查看源代码,在迭代已经开始时调用 begin() 基本上没有任何作用,因此 cursor 状态不变(并且 @987654344 @ 实际上使用底层的cursor)。我会在答案中将其标记为“我不知道”,因为这不会改变解释。
  • 好的,代码很简单。我只是在阅读“如果多次调用begin(),则返回的cursor::iterator 指向下一个剩余结果,而不是最初调用begin()的结果”并做出了我的假设从那。
猜你喜欢
  • 1970-01-01
  • 2021-12-11
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多