从无符号字符的 std::span 创建 std::string

Sim*_*mog 4 c++ stdstring c++20 std-span

我正在使用一个 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;
}
Run Code Online (Sandbox Code Playgroud)

除了使用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();
}
Run Code Online (Sandbox Code Playgroud)

该函数运行良好,使其他一切变得更简单,而无需跟踪 C 数组的大小。但是,通过一些基准测试(使用nanobench )进一步挖掘表明,新函数比经典方法慢很多倍reinterpret_cast。我的假设是基于 - 的函数for中的循环std::span是这里效率低下的。

我的问题:是否有更有效的方法将固定大小的无符号字符 C 数组从std::span变量转换为std::string


编辑:

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

相对的 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)

相对的 ns/操作 操作/秒 呃% 插入/操作 胸罩/手术衣 错过% 全部的 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点 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_bsv在基准测试中与 相同ucharspan_barry,但用std::basic_string_view<unsigned char const>参数代替std::span<unsigned char const>

Bar*_*rry 5

你要:

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

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

请注意,我从 更改span<T> const&span<T const>,有两个原因。首先,您不会改变跨度的内容,因此内部类型需要const...类似于您采用 aT const*而不是 a 的方式T*。其次,您应该span按值获取 s,因为复制它们的成本很低(除非您非常特别需要跨度的标识,但这里不需要)。

最好这样做,reinterpret_cast以便您可以使用(char const*, size_t)构造函数 - 这可以确保memcpy最终的写入是单一的。但你必须把握好时机,看看是否值得。