默认情况下,std::stack
的“基础容器”为std::deque
。因此,std::deque
的任何未定义行为都是std::stack
的未定义行为。 cppreference和其他站点在描述成员函数的行为时“有效地”使用术语。我认为这意味着它适用于所有意图和目的。因此,调用top()
和pop()
等同于调用back()
和pop_back()
,并且在空容器上调用这些是未定义的行为。
根据我的理解,它未定义行为的原因是为了保持无投掷保证。我的理由是operator[]
std::vector
具有无抛出保证,如果容器大小大于N,则是未定义的行为,但at()
具有强保证,并抛出std::out_of_range
如果n超出范围。
所以我的问题是,一些事情背后的理由是什么可能有不确定的行为,并且有一个无保证而不是有强有力的保证而是抛出异常呢?
答案 0 :(得分:14)
当允许未定义的行为时,出于效率原因,它通常是 。
如果标准指定了在访问数组越界时必须发生的事情,则会强制实现检查索引是否在边界内。矢量也是如此,它只是动态数组的包装器。
在其他情况下,允许未定义行为以允许实现自由。但这也是关于效率的(因为一些可能的实现策略在某些机器上可能比在其他机器上更有效,而C ++会让实施者选择最有效的策略,如果他们愿意的话。)
答案 1 :(得分:2)
根据Herb Sutter,一个明显的原因是效率。他指出,该标准并未对operator[]
的异常规范强加任何要求,或者是否要求绑定检查。这取决于实施。
另一方面,
vector<T>::operator[]()
是允许的,但不允许 必需,执行边界检查。没有一丝措辞 在标准的operator[]()
规范中说了什么 关于边界检查,但是没有任何要求 有一个异常规范,所以你的标准库实现者 可以自由地将边界检查添加到operator[]()
。所以,如果你使用operator[]()
要求一个不在向量中的元素,你是 在你自己,标准不保证将会是什么 发生(虽然您的标准库实现的文档 可能) - 您的程序可能会立即崩溃,呼叫operator[]()
可能会抛出异常,或者事情似乎有效 偶尔和/或神秘地失败。鉴于边界检查可以保护我们免受许多常见问题的影响, 为什么
operator[]()
不需要执行边界检查?该 简短的回答是:效率。总是检查边界会导致 (可能是轻微的)所有程序的性能开销,即使是那些 从不违反界限。 C ++的精神包含了格言 而且,你不应该为你不使用的东西付钱,等等operator[]()
不需要边界检查。在这种情况下我们 有另外一个理由想要效率:向量是有意的 要使用而不是内置数组,因此应该是有效的 作为内置数组,不进行边界检查。如果你想成为 确保检查边界,改为使用at()
。
如果您对性能优势感到好奇,请参阅以下两个问题:
共识似乎是operator[]
更有效率(因为std::vector
只是动态数组的包装器,operator[]
应该像调用它一样高效一个数组。)Herb Sutter似乎暗示它是否异常安全取决于编译器供应商。