提问人:glades 提问时间:8/30/2022 更新时间:8/28/2023 访问量:476
在不初始化内存的情况下调整 std::string 的大小
resize std::string without initializing memory
问:
我正在使用 C 例程写入 std::string 的数据字段。这是一个人为的例子,实际上我通过网络得到了一个非 null 终止的字符串及其大小:
#include <string>
#include <cstdio>
#include <cstring>
const char* str = "Some examplory string..";
int main()
{
std::string some_str;
some_str.resize(strlen(str));
std::strcpy(&some_str.front(), str);
printf(some_str.c_str());
}
现在,根据 cppref 的说法,没有重载,只是调整大小,并且不使用“\0”或其他东西初始化内存。但是,如果我所做的只是再次覆盖它,我为什么要为此付费呢?这能以某种方式避免吗?resize()
答:
2赞
Ignacio Sharpe
8/28/2023
#1
对于 c++23,我们有resize_and_overwrite
对于旧标准,让我们破解它!
它在 gcc5+、clang12+、msvc 1921+ 上运行良好
// Our hacker function, resize a std::string without initializing memory
// (but still has '\0' in the end of string)
inline void resize(std::string& str, std::size_t sz);
#if __cpp_lib_string_resize_and_overwrite >= 202110L
inline void resize(std::string& str, std::size_t sz) {
str.resize_and_overwrite(sz, [](char*, std::size_t sz) {
return sz;
});
}
#elif (defined(__clang_major__) && __clang_major__ <= 11) ||(defined(_MSC_VER)&& _MSC_VER<=1920)
// old clang has bug in global friend function. discard it.
// old msvc don't support visit private, discard it.
inline void resize(std::string& str, std::size_t sz) { str.resize(sz); }
#else
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
template <typename Money_t, Money_t std::string::* p>
class string_thief {
public:
friend void string_set_length_hacker(std::string& bank, std::size_t sz) {
(bank.*p)(sz);
}
};
#elif defined(_MSVC_STL_VERSION)
template <typename Money_t, Money_t std::string::* p>
class string_thief {
public:
friend void string_set_length_hacker(std::string& bank, std::size_t sz) {
(bank.*p)._Myval2._Mysize = sz;
}
};
#endif
#if defined(__GLIBCXX__) // libstdc++
template class string_thief<void(std::string::size_type),
&std::string::_M_set_length>;
#elif defined(_LIBCPP_VERSION)
template class string_thief<void(std::string::size_type),
&std::string::__set_size>;
#elif defined(_MSVC_STL_VERSION)
template class string_thief<decltype(std::string::_Mypair),
&std::string::_Mypair>;
#endif
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
void string_set_length_hacker(std::string& bank, std::size_t sz);
#endif
inline void resize(std::string& str, std::size_t sz) {
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
str.reserve(sz);
string_set_length_hacker(str, sz);
str[sz] = '\0';
#else
str.resize(sz);
#endif
}
#endif
1赞
HolyBlackCat
8/28/2023
#2
在 C++23 中,您可以使用 std::string::resize_and_overwrite
。例:
#include <cstring>
#include <iostream>
#include <string>
int main()
{
std::string str;
std::size_t capacity = 10; // Doesn't include the final `\0`, room for it is added automatically.
str.resize_and_overwrite(capacity, [](char *ptr, std::size_t count)
{
std::strcpy(ptr, "1234567890");
// Writing the terminating `\0` is optional, the character at `ptr[return_value]` is overwritten with `\0` anyway.
return count; // Doesn't include the final `\0`.
});
std::cout << str << '\n'; // 1234567890
}
它首先分配存储字节,调用 lambda 来填充它(lambda 的第二个参数冗余地接收相同的值)。capacity
capacity
然后,lambda 返回最终字符串大小,该大小通常必须匹配,但如果 lambda 决定写入更少的字节,则可以更小。capacity
Cppreference 声明您必须初始化 lambda 中的整个字符串,否则行为是未定义的。在实践中,你是否可以不这样做(在以后填补它)是另一个问题,如果没有充分的理由,我不会冒险。
我相信不能保证它实际上用作最终字符串容量(在实践中,对于短的 SSO 字符串,它可能会有所不同)。capacity
评论
0赞
André
8/28/2023
从技术上讲,您没有回答实际问题;-)。如何在不初始化的情况下调整大小... +1 无论如何。也许您可以强调,在不覆盖的情况下调整大小是 UB(使用什么都不做的 lambda)。
0赞
HolyBlackCat
8/28/2023
̄\_(ツ)_/ ̄ @André我在倒数第二段中谈到了这一点。我会稍微调整一下措辞。
评论
std::basic_string::resize_and_overwrite