这问题还挺复杂的。
原则上不算 c_str() 要求的 NUL 就是 max_size() - 1个,取决于分配器,多了抛出 length_error :
23.4.3.2 General requirements [string.require]
1 If any operation would cause size() to exceed max_size(), that operation throws an exception object of type length_error.
理论上也就是 allocator_traits<allocator_type>::size_type 的最大值,但实际上实现经常会限制 difference_type ,因为 container requirements 对 max_size() 的结果用了 distance 调用,所以看起来会受到 difference_type 的限制。对默认通常就是 std::numeric_limits<std::ptrdiff_t>::max() 。
MSVC 自带的标准库是这样实现的,还会 / sizeof(_Elem) (但是 string 上 sizeof(char) == 1 所以看不出来)。不过,这个逻辑上是错的,因为绕开了 allocator_traits<allocator_type>::max_size() ,直到 C++23 特地在 allocator-aware container 的概念上给 std::basic_string 的实例开洞——允许无视模板参数而直接使用 std::allocator 。
libstdc++ 在 GCC 5.0 以来这里的实现直接 / 2 错得更离谱(比如 sizeof(wchar_t) == 4 的情形,wstring 其实根本就没法有这个 size ,就硬漏 length_error ),但是 string 上也看不出来;在 vector 和 deque 上没考虑而有 bug ,直到 PR 78448 后修复。但是基于节点容器上依赖 allocator 又是逻辑错误(因为 std::allocator_traits<allocator_type>::max_size() 说的是一次分配元素个数的上限,跟节点总数根本两回事),我提了 PR 104191 ,反正没修。
另一方面,把 distance 解读为受到 different_type 的限制的逻辑问题更大,因为字面要求中“最大”只取决于 size_type ,跟 difference_type 没关系。就算 container requirement 的定义要求使用 distance 的结果,返回的也不是 difference_type ,在确定无符号数只能补码表示(自从 C++20 )没理由禁止转换为确定无符号数的 size_type 。
退一步讲,就算考虑为了使 end() - begin() well-defined ,真溢出了也是用户活该,因为 distance 对随机访问迭代器定义在 b - a 上,有 precondition 存在 difference_type 的值 n 使 a + n == b 且 b == a + (b - a) ,而这已经假定不存在 overflow 。
所以诸如 SO/questions/51907247 这样 lower_bound 或者其它哪里炸了是用户自己的责任,而不是容器实现的问题—— lower_bound 的区间表述和复杂度描述也已经隐含了同样的 narrow contract 。