为什么STL中允许未定义的行为?

时间:2014-02-27 04:47:37

标签: c++ stl undefined-behavior

默认情况下,std::stack的“基础容器”为std::deque。因此,std::deque的任何未定义行为都是std::stack的未定义行为。 cppreference和其他站点在描述成员函数的行为时“有效地”使用术语。我认为这意味着它适用于所有意图和目的。因此,调用top()pop()等同于调用back()pop_back(),并且在空容器上调用这些是未定义的行为。

根据我的理解,它未定义行为的原因是为了保持无投掷保证。我的理由是operator[] std::vector具有无抛出保证,如果容器大小大于N,则是未定义的行为,但at()具有强保证,并抛出std::out_of_range如果n超出范围。

所以我的问题是,一些事情背后的理由是什么可能有不确定的行为,并且有一个无保证而不是有强有力的保证而是抛出异常呢?

2 个答案:

答案 0 :(得分:14)

当允许未定义的行为时,出于效率原因,它通常是

如果标准指定了在访问数组越界时必须发生的事情,则会强制实现检查索引是否在边界内。矢量也是如此,它只是动态数组的包装器。

在其他情况下,允许未定义行为以允许实现自由。但这也是关于效率的(因为一些可能的实现策略在某些机器上可能比在其他机器上更有效,而C ++会让实施者选择最有效的策略,如果他们愿意的话。)

答案 1 :(得分:2)

根据Herb Sutter,一个明显的原因是效率。他指出,该标准并未对operator[]的异常规范强加任何要求,或者是否要求绑定检查。这取决于实施。

  

另一方面,vector<T>::operator[]()是允许的,但不允许   必需,执行边界检查。没有一丝措辞   在标准的operator[]()规范中说了什么   关于边界检查,但是没有任何要求   有一个异常规范,所以你的标准库实现者   可以自由地将边界检查添加到operator[]()。所以,如果你使用   operator[]()要求一个不在向量中的元素,你是   在你自己,标准不保证将会是什么   发生(虽然您的标准库实现的文档   可能) - 您的程序可能会立即崩溃,呼叫   operator[]()可能会抛出异常,或者事情似乎有效   偶尔和/或神秘地失败。

     

鉴于边界检查可以保护我们免受许多常见问题的影响,   为什么operator[]()不需要执行边界检查?该   简短的回答是:效率。总是检查边界会导致   (可能是轻微的)所有程序的性能开销,即使是那些   从不违反界限。 C ++的精神包含了格言   而且,你不应该为你不使用的东西付钱,等等   operator[]()不需要边界检查。在这种情况下我们   有另外一个理由想要效率:向量是有意的   要使用而不是内置数组,因此应该是有效的   作为内置数组,不进行边界检查。如果你想成为   确保检查边界,改为使用at()

如果您对性能优势感到好奇,请参阅以下两个问题:

  1. ::std::vector::at() vs operator[] << surprising results!! 5 to 10 times slower/faster!
  2. vector::at vs. vector::operator[]
  3. 共识似乎是operator[]更有效率(因为std::vector只是动态数组的包装器,operator[]应该像调用它一样高效一个数组。)Herb Sutter似乎暗示它是否异常安全取决于编译器供应商。