为什么在 C++11 之后,std::basic_string 的 past-the-end 迭代器的取消引用仍然是 UB?

Why is dereference of past-the-end iterator of std::basic_string still UB after C++11?

提问人:998244353 提问时间:10/10/2023 最后编辑:Jarod42998244353 更新时间:10/10/2023 访问量:104

问:

众所周知,C++11 添加了一个 null 终止符(不计入大多数成员函数)。但是当我阅读 cpp ref 时,我发现 UB 的取消引用(这一段与 std::vector 的段落几乎相同)。为什么会这样? 或者这是 cpp ref 的错误(请提供文档进行验证)?std::basic_stringend()

我尝试过 GNU C++但不幸的是__gnu_debug它似乎不包含迭代器的检查器。Clang++的消毒剂也没有。std::string

C++ 迭代器 C++17 未定义行为 stdstring

评论

2赞 Stephen Newell 10/10/2023
为什么当其他电话不行时,值得特殊外壳才能正常?std::string::end()end
1赞 Red.Wave 10/10/2023
因为它必须始终是 null 字符。这意味着它与其他可以获取任意值的角色不同。如果通过取消引用迭代器来修改结束字符,则字符串最终将处于未定义状态。

答:

6赞 ecatmur 10/10/2023 #1

正确;迭代器不能是间接的,即使它是一个封闭范围。end()[data(), data() + size()]

据我所知,唯一可以在调试模式下强制执行此功能的主要编译器是 Microsoft Visual Studio:

#include <string>
int main(int argc, char* argv[]) {
    return *std::string(argv[argc - 1]).end();
}

上面编译的程序给出了以下调试断言:cl.exe a.cpp /EHsc /Zi /MDd /std:c++20 /D_ITERATOR_DEBUG_LEVEL=2

表达式:无法取消引用字符串迭代器,因为它超出了范围(例如结束迭代器)

(libstdc++ 不执行迭代器调试,以允许调试模式和发布模式之间的 ABI 兼容性;libc++ 声称执行迭代器调试,但似乎没有捕获此错误。std::stringstd::string

标准中这种看似不一致的原因是,提供 null 终止符是为了方便需要以 null 结尾的字符串的 C 样式 API;但这些是通过原始字符指针而不是迭代器访问字符串的。因此,使过去的迭代器可取消引用对此类代码没有任何帮助,并且可能会隐藏使用 C++ 样式迭代器时的错误。