`std :: string :: begin()`/`std :: string :: end()`的迭代器失效?

bam*_*s53 5 c++ iterator const-iterator c++11 c++98

#include <string>
#include <iostream>

int main() {
    std::string s = "abcdef";

    std::string s2 = s;

    auto begin = const_cast<std::string const &>(s2).begin();
    auto end = s2.end();

    std::cout << end - begin << '\n';
}
Run Code Online (Sandbox Code Playgroud)

此代码将结果begin() const与结果混合end().这些函数都不允许使任何迭代器无效.但是我很好奇是否要求end()使iterator变量无效begin实际意味着该变量begin是可用的end.

考虑一个C++ 98,写时复制的实现std::string; 非const begin()end()函数会导致复制内部缓冲区,因为这些函数的结果可用于修改字符串.所以begin上面开始时都有效ss2,而是使用非const的end()构件使之不再有效s2,产生它的容器.

上面的代码通过copy-on-write实现产生"意外"结果,例如libstdc ++.libstdc ++ 不是end - begin相同s2.size(),而是产生另一个数字.

  • 是否导致begin不再有效的迭代器s2,从中检索的容器构成"使迭代器无效"?如果你看一下迭代器的要求,它们在.end()被调用之后似乎都保留了这个迭代器,所以可能begin仍然有资格作为有效的迭代器,因此没有被无效?

  • 以上代码是否在C++ 98中定义良好?在C++ 11中,它禁止了写时复制实现?

根据我自己对规范的简要介绍,它看起来不明确,因此即使没有混合const和非const版本,也可能无法保证结果begin()end()可以一起使用.

Jam*_*nze 6

正如您所说,C++ 11在这方面与早期版本不同.在C++ 11中没有问题,因为所有允许写入时复制的尝试都被删除了.在pre-C++ 11中,您的代码会导致未定义的行为; s2.end()允许调用使现有的迭代器无效(并且在g ++中完成,也可能仍然如此).

请注意,即使s2不是副本,标准也会允许它使迭代器无效.事实上,C++ 98的CD甚至可以做出类似f( s.begin(), s.end() )s[i] == s[j]未定义的行为.这只是在最后一刻才实现的,并且进行了更正,以便只有第一次调用begin(),end()或者[]可以使迭代器无效.