使用std::span无符号字符创建std::string

4
我将使用中文进行翻译,以下是您需要翻译的内容:

我正在使用一个C库,该库使用各种固定大小的unsigned char数组作为字符串,没有空终止符。

我一直在使用以下函数将它们转换为std::string:

auto uchar_to_stdstring(const unsigned char* input_array, int width) -> std::string {
  std::string temp_string(reinterpret_cast<const char*>(input_array), width);
  temp_string.erase(temp_string.find_last_not_of(' ') + 1);

  return temp_string;
}

除了使用reinterpret_cast、需要传递数组大小以及将数组降级为指针之外,一切正常。我试图使用std::span来避免所有这些问题。

使用std::span的函数如下:

auto ucharspan_to_stdstring(const std::span<unsigned char>& input_array) -> std::string {
  std::stringstream temp_ss;

  for (const auto& input_arr_char : input_array) {
    temp_ss << input_arr_char;
  }

  return temp_ss.str();
}

该函数很好用,无需跟踪C数组的大小即可简化其他所有操作。但是,进一步深入研究并进行基准测试(使用nanobench)表明,新函数比经典的reinterpret_cast方法慢得多。我的假设是std::span函数中的for循环是这里的低效率原因。
我的问题是:是否有更有效的方法将std::span变量中的固定大小的C数组转换为std::string

编辑:

gcc 基准测试 (-O3 -DNDEBUG -std=gnu++20,nanobench,minEpochIterations=54552558,热身=100,不要优化掉)

相对值 ns/操作 操作数/秒 错误率% 每操作插入字节数 分支预测错误次数/操作 缺失率% 总计 uchar[] 转 std::string
100.0% 5.39 185,410,438.12 0.3% 80.00 20.00 0.0% 3.56 uchar
2.1% 253.06 3,951,678.30 0.6% 4,445.00 768.00 0.0% 167.74 ucharspan
1,244.0% 0.43 2,306,562,499.69 0.2% 9.00 1.00 0.0% 0.29 ucharspan_barry
72.8% 7.41 134,914,127.56 1.3% 99.00 22.00 0.0% 4.89 uchar_bsv

clang 基准测试 (-O3 -DNDEBUG -std=gnu++20, nanobench, minEpochIterations=54552558, warmup=100, doNotOptimizeAway)

相对值 纳秒/操作 操作数/秒 错误率% 每个操作的指令数 每个分支数 缺失率% 总计 uchar[] 转为 std::string
100.0% 2.13 468,495,014.11 0.2% 14.00 1.00 0.0% 1.42 uchar
0.8% 251.74 3,972,418.54 0.2% 4,477.00 767.00 0.0% 166.30 ucharspan
144.4% 1.48 676,329,668.07 0.1% 7.00 0.00 95.8% 0.98 ucharspan_barry
34.5% 6.19 161,592,563.70 0.1% 80.00 24.00 0.0% 4.08 uchar_bsv
在基准测试中,uchar_bsvucharspan_barry相同,但使用的参数是std::basic_string_view<unsigned char const>而不是std::span<unsigned char const>

为什么要使用 erase - Barry
为什么不使用std::string_view? - eerorika
@Barry,需要进行擦除操作,因为一些字符串有后缀空格需要去除。 - Simog
@Simog string_view 是有符号字符吗?不,它是 char。但这并不是我想说的重点。实际上我指的是 basic_string_view,它是一个模板。你可以直接使用 std::basic_string_view<unsigned char> - eerorika
可能由于迭代和填充分支预测器等因素,转换现在变得不真实快速。但是Barry似乎是赢家? - Sebastian
1个回答

5

您希望:

auto ucharspan_to_stdstring(std::span<unsigned char const> input_array) -> std::string {
    return std::string(input_array.begin(), input_array.end());
}

string,像其他标准库容器一样,可以从适当的迭代器对构造 - 而这就是这样的一对。由于这些是随机访问迭代器,因此这将进行单个分配等操作。

请注意,我从 span<T> const& 更改为 span<T const>,有两个原因。首先,您没有改变 span 的内容,因此内部类型需要是 const … 类似于您使用了 T const*,而不是 T*。其次,应该通过值来获取 span,因为它们很便宜(除非您非常需要 span 的身份,但在这里不需要)。

可能最好进行 reinterpret_cast,这样您就可以使用 (char const*, size_t) 构造函数 - 这个函数确保最终写入只需一个 memcpy。但你必须测定时间以确定是否值得。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接