在 C++23 中,std::string 增加了 resize_and_overwrite 成员函数,这个函数用于消除 std::string 扩容时的额外开销。下面看一个例子。
如果要将一个 std::string 初始化为 0 ~ 127 所有 ASCII 字符,在 C++23 前可能会这么写:
#include <string>
int main() {
std::string s(128, '\0');
for (std::size_t i = 0; i < s.size(); i++)
s[i] = static_cast<char>(i);
}
第一步,将 s 初始化为 128 个空字符。
第二步,使用循环为 s 的每个字节赋值。
在这个过程中,第一步的初始化是完全没有必要的,因为每个字节最终都会被新的值覆盖。
在 C++23 以前,第一步是无法省略的,简单来说,STL 提供的抽象是“元素管理器”,而不是“内存管理器”,因此 STL 保存的是已经被初始化的元素,而不会是“刚被分配还未初始化的内存”。
注意,以下写法是错误的:
#include <string>
int main() {
std::string s;
s.reserve(128);
for (std::size_t i = 0; i < s.size(); i++)
s[i] = static_cast<char>(i);
}
reserve
成员函数仅改变底层分配的内存大小,而不改变 std::string 中的元素数量。在调用 reserve
后,字符串 s 依然是空字符串,此时往里面写内容属于越界写。
在 C++23 以后,使用 resize_and_overwrite
函数可以省略无意义的初始化。下面看示例:
#include <string>
int main() {
std::string s;
s.resize_and_overwrite(128, [](char *s, std::size_t n){
for (int i = 0; i < 128; i++)
s[i] = static_cast<char>(i);
return n;
});
}
resize_and_overwrite
接受两个参数,第一个参数 n 是最大分配内存,第二个参数 op 是一个可调用对象(函数指针、重载 ()
的类、lambda 表达式等),这个对象的调用返回一个整型值。
在调用 resize_and_overwrite
时,首先会为字符串分配 n 个字节的空间,接着会调用可调用对象 op,将 s.data() 和 n 作为参数传递给 op,最后使用 op 的返回值作为字符串的实际元素数量。
结合上面的代码,首先 s 分配 128 字节的空间,然后调用第二个参数 lambda 表达式,将 s.data() 和 128 作为 lambda 表达式的参数。在 lambda 表达式里,使用 for 循环向字符串中写入 0 ~ 127 共 128 个 ASCII 字符,最后返回 n,也就是 128,表示字符串的实际元素数量是 128。调用完毕后,s.size() 为 128,s.capacity() 为 1024。
在使用这个函数时有几点需要注意:
- 可调用对象 op 的返回值不能大于第一个参数 n
- 可调用对象 op 里执行的操作不能越界写,即不能写超过 n 个字符
- 在可调用对象 op 里必须确保每个元素都被初始化
- 可调用对象 op 不能抛出异常,否则是未定义行为
使用 resize_and_overwrite
避免了字符串 resize 时多余的初始化,这也是 C++ 零成本抽象理念的具体体现。
3 条评论
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
新盘首开 新盘首开 征召客户!!!
新盘 上车集合 留下 我要发发 立马进裙