【问题标题】:Should it be possible to add to an iterator of an empty deque in C++?是否可以在 C++ 中添加到空双端队列的迭代器?
【发布时间】:2020-06-24 00:23:24
【问题描述】:

以下是导致问题的示例:

#include <deque>

int main() {
    std::deque<int> has_data = {1, 2, 3};
    std::deque<int>::iterator iter1 = has_data.begin() + 5; // This works fine
    
    std::deque<int> had_data = {4, 5, 6};
    had_data.clear();
    std::deque<int>::iterator iter2 = had_data.begin() + 5; // This also works fine
    
    std::deque<int> is_empty;
    std::deque<int>::iterator iter3 = is_empty.begin() + 5; // This causes a segfault
}

如果双端队列以前从未包含任何元素,添加到空双端队列的迭代器似乎是一个问题。

我很好奇这是否是 STL 中的错误,或者我只是以导致未定义行为的方式使用它。我只在使用 Xcode(GUI 和命令行)编译时遇到这个问题。我也试过在 Linux 上使用 GCC 6.2.0 版,但那里似乎不存在问题。

【问题讨论】:

  • 对我来说听起来像是一个实现错误。另外,你为什么要增加迭代器过去 end?当 deque 只有 3 个元素(或 0 个元素)时使用 + 5 无论如何都是错误的。
  • 原因是(至少在我的程序中)用户可以输入无效的索引。当他们这样做时,它将检查以确保生成的迭代器在范围内,如果不在则显示错误消息。如果程序段错误,它就不能这样做。我目前的解决方法是在生成迭代器之前检查容器是否为空。虽然这工作正常,但我想检查它是否是一个实际的错误,或者只是我很愚蠢。如果是前者,我计划将其报告给适当的来源。
  • 是什么阻止了您在将索引应用于迭代器之前对其进行验证?通过检查索引是否为0 &lt;= index &lt; deque::size() 很容易做到这一点。始终在使用之前验证/清理用户输入。
  • 顺便说一句,在实际标准中没有提到标准模板库或STL,这个术语来自很久以前。如今它只是“标准”:-)
  • 我真的不会那样写我的代码。我会对照std::deque::size()检查输入的索引。

标签: c++ stl iterator c++17 stddeque


【解决方案1】:

您在这里遇到的问题是,使用+ 运算符将迭代器向前跳转到容器末端的位置是未定义的行为。因此,您正在执行的每个迭代器操作都会导致未定义的行为,而且恰好是前两种情况巧合地没有崩溃,而最后一种情况却发生了。

(至于为什么这是未定义的行为:C++17 标准要求(第 27.2.3/2 节),要递增迭代器,迭代器必须是 dereferencable,在某种意义上在增加迭代器之前必须安全和合法地取消引用它然后它说(§27.2.7/1)如果你在迭代器上使用++=,它应该具有与你相同的效果迭代地执行++ 适当的次数。因此,如果您将迭代器跳过超过容器末尾的一步,则在某些时候您将到达容器的末尾,其中迭代器不可取消引用并且会触发未定义的行为。)

希望这会有所帮助!

【讨论】:

  • 就在第二段开头,我会说“你增加一个迭代器之前”。我看到这一点稍后会更清楚,但在递增之前或之后是否需要这种 derferencabilty 是模棱两可的。
【解决方案2】:

我很好奇这是否是 STL 中的错误,或者我只是以导致未定义行为的方式使用它。我只在使用 Xcode(GUI 和命令行)编译时遇到这个问题。

您正在导致未定义的行为。

我也曾在 Linux 上使用 GCC 6.2.0 版本进行过尝试,但那里似乎不存在问题。

不,未定义的行为在那里以不同的方式表现出来;你尝试它的时候它并没有导致 SEGFAULT。 (不过下次你可以试试)

【讨论】:

    猜你喜欢
    • 2015-07-18
    • 2015-06-26
    • 1970-01-01
    • 2017-05-20
    • 2010-12-12
    • 2014-03-14
    • 1970-01-01
    • 2010-10-29
    • 2013-06-03
    相关资源
    最近更新 更多