28

我见过很多次std::string::operator[]没有做任何边界检查。甚至string::at 和 string::operator[] 有什么区别?,在 2013 年问,答案说operator[]不做任何边界检查。

我的问题是,如果我查看 [string.access] 中的标准(在本例中为草案 N3797),我们有

const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
  1. 要求: pos <= size()
  2. 返回: *(begin() + pos)如果pos < size()。否则,返回对charT具有 value类型的对象的引用charT(),其中修改对象会导致未定义的行为。
  3. 抛出:什么都没有。
  4. 复杂性:恒定时间。

这使我相信operator[]必须进行某种边界检查以确定它是否需要返回 string 的元素或 default charT。这个假设是否正确并且operator[]现在需要进行边界检查?

4

5 回答 5

45

措辞有点混乱,但如果你仔细研究它,你会发现它实际上非常精确。

它是这样说的:

  • 前提条件是 to 的参数[]要么是 = n要么是 < n
  • 假设满足前提条件:
    • 如果它是 < n那么你得到你要求的字符。
    • “否则”(即如果它是n)然后你得到charT()(即空字符)。

但是,当您打破先决条件时,没有定义任何规则,并且可以通过在位置n处实际存储 a 来隐式地满足对 = n的检查(但没有明确要求)charT()

所以实现不需要执行任何边界检查……而普通的则不需要。

于 2016-07-21T14:11:24.227 回答
14

operator[]已经进行了某种边界检查以确定...

不,它没有。有前提

要求:pos <= size()。

它可以假设它总是可以返回字符串的一个元素。如果不满足此条件:未定义的行为。

operator[]可能只是将指针从字符串的开头增加 pos。如果字符串更短,那么它只会返回对字符串后面数据的引用,无论它可能是什么。就像简单 C 数组中的经典越界。

为了完善pos == size()它可以charT在其内部字符串数据的末尾分配一个额外的情况。因此,仅在不进行任何检查的情况下增加指针,仍会提供所述行为。

于 2016-07-21T14:09:58.443 回答
4

首先,有一个 requires 子句。如果您违反了 requires 子句,您的程序将以未定义的方式运行。那就是pos <= size()

所以语言只定义了在这种情况下会发生什么。

下一段指出,对于pos < size(),它返回对字符串中元素的引用。而对于pos == size(),它返回对使用 value 构造的默认charT值的引用charT()

虽然这可能看起来像边界检查,但实际上实际发生的是std::basic_string分配一个比请求大一个的缓冲区,并用charT(). 然后[]简单地做指针算术。

我试图想出一种方法来避免这种实施。虽然标准没有强制要求,但我无法说服自己存在替代方案。有一些烦人的东西.data()使得很难避免使用单个缓冲区。

于 2016-07-21T14:20:14.053 回答
2

这个标准容器的操作符模拟普通数组的操作符 [] 的行为。所以它不做任何检查。但是在调试模式下,相应的库可以提供这种检查。

如果要检查索引,请改用成员函数at()

于 2016-07-21T14:09:31.490 回答
1

http://en.cppreference.com/w/cpp/string/basic_string/operator_at

返回对指定位置 pos 的字符的引用。不执行边界检查。

(强调我的)。

如果要进行边界检查,请使用std::basic_string::at

该标准意味着实现需要提供边界检查,因为它基本上描述了未经检查的数组访问的作用。

如果您在范围内访问,则已定义。如果你走出去,你会触发未定义的行为。

于 2016-07-21T14:07:45.593 回答