为什么我不能使用std::string迭代器创建std::string_view?

6
可以轻松地从std::string创建std::string_view。但如果想要使用std::string的迭代器创建std::string范围的字符串视图,则不起作用。
这是我尝试过的代码:https://gcc.godbolt.org/z/xrodd8PMq
#include <iostream>
#include <string>
#include <string_view>
#include <iterator>

int main()
{
    std::string str{"My String"};
    std::string_view strView{str};  // works
    //std::string_view strSubView{str.begin(), str.begin() + 2}; // error
}

当然,我们可以从str中提取子字符串并使用它来创建一个字符视图strSubView,但这会额外创建一个字符串。

我发现std::basic_string_view的第五个构造函数接受迭代器范围。

template<class It, class End>
constexpr basic_string_view(It first, End last);

这只涉及到std::string的迭代器还是只有std::basic_string_view本身?如果不是因为std::string的迭代器,那么我们为什么不应该拥有一个呢?毕竟字符串视图:

描述了一个可以引用连续的字符对象序列的对象!

考虑到连续的字符序列的范围,难道我们不应该计数吗?


那个构造函数是在C++20中添加的。如果您使用C++17编译器进行编译,则不存在该函数。如果您的目标是C++20,它将起作用。请参见此链接 - Caleth
我们可以从字符串中提取子串...但会多创建一个字符串。std::string_view也可以创建子串,例如:strSubView = strView.substr(0, 2);。这与您尝试的操作相似。 - Drew Dormann
@Caleth 噢,是的,那个我看到了,但我没有注意到cppreference中的语言标签。这意味着在C++17中没有办法做到吗? - Const
3个回答

6

这个构造函数是在 C++20 中添加的。如果您使用的是 C++17 编译器,则不存在该构造函数。

您可以编写一个执行相同操作的函数。

std::string_view range_to_view(std::string::iterator first, std::string::iterator last) {
    return first != last ? { first.operator->(), last - first } : { nullptr, 0 };
}

我希望无论我在哪里调用 range_to_view,都能够更方便地构造 std::string_view。 - Const
2
@Const是一个未实例化的临时变量,它会在调用点就地构造。 - Caleth
如果 first 指向末尾,那么 operator-> 将失败。 - Dmytro Ovdiienko
@DmytroOvdiienko 不,你会得到一个指向末尾后面的指针。 - Caleth

4
为什么我不能从std::string迭代器创建std::string_view?
自C++20开始,您可以这样做。
但是只能使用适当值类型的连续迭代器。字符串和字符串视图迭代器都是可接受的。确切的约束条件在文档中列出。
这意味着在C++17中没有办法吗?
不能使用不存在的构造函数,但有几种方法可以获得所需的字符串视图。下面是其中的一种方法:
std::string_view strSubView = strView.substr(0, 2);

或者不使用中间变量:
std::string_view strSubView = std::string_view{str}.substr(0, 2);

或者如果您只有这些迭代器,并且无法访问字符串:

std::string_view strSubView {&*first, last - first};

感谢您的解释,我更喜欢使用辅助函数,直到我能够使用C++20。 - Const
1
&*first may crash if first points to end - Dmytro Ovdiienko
"&*first" 在 Caleth 的回答中等同于 "first.operator->()",对吗? - Peter - Reinstate Monica
@Peter-ReinstateMonica 有效地是的。 - eerorika

0

由于解引用迭代器不安全(即在指向末尾的迭代器上调用operator*operator->可能会导致应用程序崩溃),在C++17中我看到的唯一方法是调用string_view::substr(),并仅使用迭代器来计算大小和索引。

constexpr std::string_view make_string_view(std::string_view str, 
                                            std::string_view::iterator first, 
                                            std::string_view::iterator last) noexcept
{
    return str.substr(first - str.begin(), last - first);
}

对于 std:string,请使用 std::string::data() 作为初始指针。

std::string_view make_string_view(std::string const& str, 
                                  std::string::const_iterator first, 
                                  std::string::const_iterator last) noexcept
{
    return std::string_view(str.data() + (first - str.begin()), last - first);
}


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