连接 string_view 对象

sim*_*ame 10 c++ string-concatenation string-view c++17

我一直std::string_view在一些旧代码中添加 s 来表示配置参数等字符串,因为它提供了只读视图,由于不需要复制,速度更快。

string_view但是,由于operator+未定义,因此无法将两个连接在一起。我看到这个问题有几个答案表明它是一个疏忽,并且有一个建议添加它。但是,这是添加 astring和 a string_view,大概如果实现的话,生成的串联将是std::string

添加两个string_view也属于同一类别吗?如果不是,为什么不string_view支持添加两个?

样本

std::string_view s1{"concate"};
std::string_view s2{"nate"};
std::string_view s3{s1 + s2};
Run Code Online (Sandbox Code Playgroud)

这是错误

error: no match for 'operator+' (operand types are 'std::string_view' {aka 'std::basic_string_view<char>'} and 'std::string_view' {aka 'std::basic_string_view<char>'})
Run Code Online (Sandbox Code Playgroud)

Cor*_*mer 13

视图与跨度类似,因为它不拥有数据,顾名思义,它只是数据的视图。要连接字符串视图,您首先需要构造 astd::string然后才能连接。

std::string s3 = std::string(s1) + std::string(s2);
Run Code Online (Sandbox Code Playgroud)

请注意,这s3将是 astd::string而不是 a std::string_view,因为它拥有此数据。

  • @simplename 不,那么数据将存储在哪里?一旦“std::string”超出范围,任何“string_view”都将不再有用。您必须存储`std::string`。你可以为它创建任意数量的“string_view”——但你需要保持它的活力。 (3认同)
  • @simplename 如果我有时间,我会将其作为完整答案发布,但如果您决心尽可能长时间地将所有内容保留为“sv”,您可以创建一个聚合“sv”的简单类。但坦率地说,我不会担心这一点,除非您绝对确定这会减慢您的系统速度并且解决方案更快 (2认同)

Enr*_*lis 8

Astd::string_view是 的别名std::basic_string_view<char>,它是std::basic_string_view基于特定类型字符的模板,即char

\n

但它看起来是什么样子呢?

\n

除了相当多的有用的成员函数,例如findsubstr和其他(如果与 STL 提供的其他容器/字符串之类的东西相比,也许它是一个普通的数字), ,std::basic_string_view<_CharT>_CharT通用的char类型,只有 2 个数据成员,

\n
// directly from my /usr/include/c++/12.2.0/string_view\n      size_t        _M_len;\n      const _CharT* _M_str;\n
Run Code Online (Sandbox Code Playgroud)\n

即一个constant 指针来_CharT指示视图从哪里开始,以及一个size_t(适当类型的数字)来指示视图从多长时间开始_M_str\ 的指针对象开始多长时间的 a (适当类型的数字)。

\n

换句话说,字符串视图只知道它从哪里开始以及它有多长,因此它表示一系列在内存中连续的char类似实体。仅使用两个这样的成员,就无法表示由不连续的子字符串组成的字符串。

\n

但换句话来说,如果你想创建一个std::string_view,你需要能够知道char它有多少 s 长以及从哪个位置开始。你能说出从哪里s1 + s2开始以及应该有多少个字符吗?想想看:你不能,因为s1s2不相邻。

\n

也许图表可以提供帮助。

\n

假设这些代码行

\n
std::string s1{"hello"};\nstd::string s2{"world"};\n
Run Code Online (Sandbox Code Playgroud)\n

s1s2就它们的内存位置而言,它们是完全不相关的对象;它们是这样的:

\n
                           &s2[0]\n                             |\n                             | &s2[1]\n                             |   |\n&s1[0]                       |   | &s2[2]\n  |                          |   |   |\n  | &s1[1]                   |   |   | &s2[3]\n  |   |                      |   |   |   |\n  |   | &s1[2]               |   |   |   | &s2[4]\n  |   |   |                  |   |   |   |   |\n  |   |   | &s1[3]           v   v   v   v   v\n  |   |   |   |            +---+---+---+---+---+\n  |   |   |   | &s1[4]     | w | o | r | l | d |\n  |   |   |   |   |        +---+---+---+---+---+\n  v   v   v   v   v\n+---+---+---+---+---+\n| h | e | l | l | o |\n+---+---+---+---+---+\n
Run Code Online (Sandbox Code Playgroud)\n

我故意将它们绘制为未对齐,这意味着&s1[0], 开始的内存位置s1, 和&s2[0], 开始的内存位置s2,彼此无关。

\n

现在,想象您创建两个像这样的字符串视图:

\n
std::string_view sv1{s1};\nstd::string_view sv2(s2.begin() + 1, s2.begin() + 4);\n
Run Code Online (Sandbox Code Playgroud)\n

_M_str根据两个实现定义的成员和,它们的外观如下_M_len

\n
                                &s2[0]\n                                  |\n                                  | &s2[1]\n                                  |   |\n     &s1[0]                       |   | &s2[2]\n       |                          |   |   |\n       | &s1[1]                   |   |   | &s2[3]\n       |   |                      |   |   |   |\n       |   | &s1[2]               |   |   |   | &s2[4]\n       |   |   |                  |   |   |   |   |\n       |   |   | &s1[3]           v   v   v   v   v\n       |   |   |   |            +---+---+---+---+---+\n       |   |   |   | &s1[4]     | w | o | r | l | d |\n       |   |   |   |   |        +---+---+---+---+---+\n       v   v   v   v   v            \xc2\xb7 ^         \xc2\xb7\n     +---+---+---+---+---+          \xc2\xb7 |         \xc2\xb7\n     | h | e | l | l | o |        +---+         \xc2\xb7\n     +---+---+---+---+---+        | \xc2\xb7           \xc2\xb7\n     \xc2\xb7 ^                 \xc2\xb7        | \xc2\xb7 s2._M_len \xc2\xb7\n     \xc2\xb7 |                 \xc2\xb7        | <----------->\n   +---+                 \xc2\xb7        |\n   | \xc2\xb7                   \xc2\xb7        +-- s2._M_str\n   | \xc2\xb7       s1._M_len   \xc2\xb7\n   | <------------------->\n   |\n   +-------- s1._M_str\n
Run Code Online (Sandbox Code Playgroud)\n

鉴于上述情况,您能看出预期有什么问题吗?

\n
std::string_view s3{s1 + s2};\n
Run Code Online (Sandbox Code Playgroud)\n

作品?

\n

您如何定义s3._M_strs3._M_len(基于s1._M_strs1._M_lens2._M_strs2._M_len),以便它们代表关于 的视图"helloworld"

\n

你不能,因为"hello""world"位于两个不相关的内存区域。

\n


Rai*_*der 6

std::string_view不拥有任何数据,它只是一个视图。如果您想连接两个视图以获得连接视图,您可以使用boost::join()Boost 库。但结果类型将不是std::string_view.

#include <iostream>
#include <string_view>
#include <boost/range.hpp>
#include <boost/range/join.hpp>

void test()
{
    std::string_view s1{"hello, "}, s2{"world"};
    auto joined = boost::join(s1, s2);

    // print joined string
    std::copy(joined.begin(), joined.end(), std::ostream_iterator(std::cout, ""));
    std::cout << std::endl;

    // other method to print
    for (auto c : joined) std::cout << c;
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

C++23 在标准库中加入了范围,名称为std::ranges::views::join_with_view

#include <iostream>
#include <ranges>
#include <string_view>

void test()
{
    std::string_view s1{"hello, "}, s2{"world"};
    auto joined = std::ranges::views::join_with_view(s1, s2);

    for (auto c : joined) std::cout << c;
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)