c++ 串联string_view对象

aiqt4smr  于 2023-08-09  发布在  其他
关注(0)|答案(4)|浏览(165)

我已经在一些旧代码中添加了std::string_view s来表示字符串,比如配置参数,因为它提供了一个只读视图,由于不需要复制,所以速度更快。
但是,不能将两个string_view连接在一起,因为没有定义operator+。我看到this question有几个答案,说明这是一个疏忽,并有一个建议,在添加。但是,这是为了添加stringstring_view,假设如果实现了这一点,则产生的串联将是std::string
添加两个string_view是否也属于同一类别?如果不是,为什么不支持添加两个string_view
样品

std::string_view s1{"concate"};
std::string_view s2{"nate"};
std::string_view s3{s1 + s2};

字符串
这里有个错误

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>'})

5t7ly7z5

5t7ly7z51#

视图类似于span,因为它不拥有数据,顾名思义,它只是数据的视图。要连接字符串视图,首先需要构造一个std::string,然后才能连接。

std::string s3 = std::string(s1) + std::string(s2);

字符串
请注意,s3将是std::string而不是std::string_view,因为它将拥有此数据。

mwyxok5s

mwyxok5s2#

std::string_viewstd::basic_string_view<char>的别名,std::basic_string_view<char>是以特定类型的字符为模板的std::basic_string_view,即char的值。
但它看起来像什么?
除了大量有用的成员函数,如findsubstr和其他(如果与STL提供的其他容器/字符串类似的东西相比,它可能是一个普通的数字),std::basic_string_view<_CharT>_CharT是通用的char类型,只有2个数据成员,

// directly from my /usr/include/c++/12.2.0/string_view
      size_t        _M_len;
      const _CharT* _M_str;

字符串
即,指向X1 M10 N1 X的X1 M9 N1 XANT指针,以指示视图从哪里开始,以及X1 M11 N1 X(适当类型的数字),以指示视图从X1 M12 N1 X的指针开始多长时间。
换句话说,一个字符串视图只知道它从哪里开始,有多长,所以它表示一个类似char的实体序列,这些实体在内存中是连续的。只有两个这样的成员,您不能表示由不连续的子字符串组成的字符串。
换句话说,如果你想创建一个std::string_view,你需要知道它有多少个char,以及从哪个位置开始。你能告诉s1 + s2应该从哪里开始,它应该有多少个字符长吗?想想看吧:你不能,因为s1s2不相邻。
也许一个图表可以帮助。
假设这几行代码

std::string s1{"hello"};
std::string s2{"world"};


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

&s2[0]
                             |
                             | &s2[1]
                             |   |
&s1[0]                       |   | &s2[2]
  |                          |   |   |
  | &s1[1]                   |   |   | &s2[3]
  |   |                      |   |   |   |
  |   | &s1[2]               |   |   |   | &s2[4]
  |   |   |                  |   |   |   |   |
  |   |   | &s1[3]           v   v   v   v   v
  |   |   |   |            +---+---+---+---+---+
  |   |   |   | &s1[4]     | w | o | r | l | d |
  |   |   |   |   |        +---+---+---+---+---+
  v   v   v   v   v
+---+---+---+---+---+
| h | e | l | l | o |
+---+---+---+---+---+


我故意把它们画得不对齐,意思是&s1[0]s1开始的内存位置,和&s2[0]s2开始的内存位置,彼此没有任何关系。
现在,假设你创建了两个字符串视图,如下所示:

std::string_view sv1{s1};
std::string_view sv2(s2.begin() + 1, s2.begin() + 4);


以下是它们的外观,根据两个实现定义的成员_M_str_M_len

&s2[0]
                                  |
                                  | &s2[1]
                                  |   |
     &s1[0]                       |   | &s2[2]
       |                          |   |   |
       | &s1[1]                   |   |   | &s2[3]
       |   |                      |   |   |   |
       |   | &s1[2]               |   |   |   | &s2[4]
       |   |   |                  |   |   |   |   |
       |   |   | &s1[3]           v   v   v   v   v
       |   |   |   |            +---+---+---+---+---+
       |   |   |   | &s1[4]     | w | o | r | l | d |
       |   |   |   |   |        +---+---+---+---+---+
       v   v   v   v   v            · ^         ·
     +---+---+---+---+---+          · |         ·
     | h | e | l | l | o |        +---+         ·
     +---+---+---+---+---+        | ·           ·
     · ^                 ·        | · s2._M_len ·
     · |                 ·        | <----------->
   +---+                 ·        |
   | ·                   ·        +-- s2._M_str
   | ·       s1._M_len   ·
   | <------------------->
   |
   +-------- s1._M_str


考虑到上面的情况,你能看出期望这样做有什么错吗

std::string_view s3{s1 + s2};


工作?
如何定义s3._M_strs3._M_len(基于s1._M_strs1._M_lens2._M_strs2._M_len),使它们表示"helloworld"上的视图?
不能,因为"hello""world"位于两个不相关的内存区域。

wi3ka0sx

wi3ka0sx3#

std::string_view不拥有任何数据,它只是一个视图。如果要连接两个视图以获得连接视图,可以使用Boost库中的boost::join()。但结果类型将不是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;
}

字符串
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;
}

hgc7kmma

hgc7kmma4#

std::string_view是一个轻量级的、不拥有角色的视图。
要获得一个连接多个字符串视图的视图,我们可以使用C++20中引入的 join 视图适配器:

auto const joined = std::views::join(std::array{s1, s2});

字符串
这给了我们一个视图对象,可以使用标准算法或基于范围的for来迭代。它可以转换为std::string对象(但不能直接转换为std::string_view,因为这需要我们将内容复制到某个地方以使它们连续)。
完整演示:

#include <algorithm>
#include <array>
#include <ranges>
#include <string_view>

int main()
{
    using namespace std::string_view_literals;

    std::string_view s1{"concate"};
    std::string_view s2{"nate"};

    return !std::ranges::equal(std::views::join(std::array{s1, s2}),
                               std::string_view{"concatenate"});
}

相关问题