Ded*_*tor 21 c++ string iterator stdstring language-lawyer
它是有效的创建一个迭代end(str)+1的std::string?
如果不是,为什么不呢?
这个问题是限制在C++ 11以后,因为在预C++ 11中的数据已经存储在连续块中的任何但是罕见POC玩具的实现,该数据没有具有要被存储的方式.
而且我认为这可能会产生重大影响.
std::string我推测的任何其他标准容器之间的显着差异在于它总是包含一个元素size,而不是它的零终止符,以满足要求.c_str().
21.4.7.1 basic_string访问器[string.accessors]
Run Code Online (Sandbox Code Playgroud)const charT* c_str() const noexcept; const charT* data() const noexcept;1返回:一个指针
p,p + i == &operator[](i)用于每个iin[0,size()].
2复杂性:恒定时间.
3要求:程序不得更改存储在字符数组中的任何值.
尽管如此,尽管它应该保证所述表达式是有效的,但为了保持与零终止字符串的一致性和互操作性,如果没有别的话,我发现的唯一一段对此表示怀疑:
21.4.1 basic_string一般要求[string.require]
4对象中的类似char的
basic_string对象应连续存储.也就是说,任何basic_string对象s,身份&*(s.begin() + n) == &*s.begin() + n应持的所有值n这样0 <= n < s.size().
(所有引用均来自C++ 14最终草案(n3936).)
Mat*_* M. 10
TL; DR: s.end() + 1未定义的行为.
std::string 是一种奇怪的野兽,主要是出于历史原因:
\0报告的长度之外存在另外的字符strlen.std::string在C++ 03中,这导致103个成员函数,从那以后增加了一些.
因此,应该预期不同方法之间的差异.
已经在基于索引的界面中出现差异:
§21.4.5[string.access]
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);1/ 要求:
pos <= size()
const_reference at(size_type pos) const;reference at(size_type pos);5/ 抛出:
out_of_range如果pos >= size()
是的,你读了这个权利,s[s.size()]在s.at(s.size())抛出out_of_range异常时返回对NUL字符的引用.如果有人告诉你要更换operator[]by的所有用途,at因为它们更安全,请注意string陷阱......
那么,迭代器呢?
§21.4.3[string.iterators]
iterator end() noexcept;
const_iterator end() const noexcept;
const_iterator cend() const noexcept;2/ 返回:迭代器,它是过去的结束值.
非常平淡无奇.
所以我们必须参考其他段落.指针由提供
§21.4[basic.string]
3 /支持的迭代器
basic_string是随机访问迭代器(24.2.7).
虽然§17.6[要求]似乎没有任何相关的东西.因此,字符串迭代器只是普通的旧迭代器(你可能会感觉到它的发展方向......但是因为我们走到了这一步,所以一路走下去).
这导致我们:
24.2.1 [iterator.requirements.general]
5 /正如指向数组的常规指针一样,保证指针值指向数组的最后一个元素,因此对于任何迭代器类型,都有一个迭代器值指向相应序列的最后一个元素.这些值称为past-the-end值.定义
i表达式的迭代器的值*i称为可解除引用.库从不假设过去的值是可解除引用的.[...]
所以,*s.end()是不正确的.
24.2.3 [input.iterators]
2 /表107 - 输入迭代器要求(除迭代器外)
名单先决条件++r,并r++认为r是dereferencable.
Forward迭代器,Bidirectional迭代器和Random迭代器都没有解除这个限制(并且都表明它们继承了其前任的限制).
另外,为了完整性,在24.2.7 [random.access.iterators]中,表111 - 随机访问迭代器要求(除了双向迭代器)列出了以下操作语义:
r += n相当于[inc | dec]记忆r n时间a + n并n + a等同于复制a然后应用于+= n副本同样的-= n和- n.
因此s.end() + 1是未定义的行为.
返回:一个指针
p,p + i == &operator[](i)用于每个iin[0,size()].
std::string::operator[](size_type i)被指定为基准返回"到类型的对象charT与值charT()时i == size(),因此,我们知道,该指针指向的对象.
5.7声明"出于[operator +和 - ]的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型."
所以我们有一个非数组对象,规范保证一个指针通过它可以表示.所以我们知道std::addressof(*end(str)) + 1必须具有代表性.
然而,这不是一个保证std::string::iterator,并且在规范中的任何地方都没有这样的保证,这使得它成为未定义的行为.
(请注意,这与' *end(str) + 1格式错误'不同.实际上是格式良好的.)
迭代器可以并且确实实现检查逻辑,当您执行增量end()迭代器等操作时会产生各种错误.这实际上是Visual Studios调试迭代器所做的事情end(str) + 1.
#define _ITERATOR_DEBUG_LEVEL 2
#include <string>
#include <iterator>
int main() {
std::string s = "ssssssss";
auto x = std::end(s) + 1; // produces debug dialog, aborts program if skipped
}
Run Code Online (Sandbox Code Playgroud)
如果不是,为什么不呢?
用于与零终止字符串的一致性和互操作性,如果没有别的话
C++指定了与C兼容的一些特定内容,但这种向后兼容性仅限于支持实际用C语言编写的内容.C++不一定试图采用C语义并使新结构以某种类似的方式运行.应该std::vector衰减到迭代器只是为了与C的数组衰减行为一致吗?
我会说它end(std) + 1是未定义的行为,因为尝试以std::string这种方式约束迭代器没有任何价值.没有传统的C代码可以实现这一点,C++需要与之兼容,并且应该阻止新代码执行此操作.
应该阻止新代码依赖它...为什么?[...]什么不允许它在理论上买你,这在实践中看起来如何?
不允许它意味着实现不必支持增加的复杂性,复杂性提供零证明的价值.
事实上,在我看来,支持end(str) + 1具有负值,因为尝试使用它的代码将基本上创建与C代码相同的问题,这无法确定何时考虑空终止符.对于两种语言,C都有足够的缓冲区大小错误.