使用string_view构造istringstream失败。

8

我无法将std::string_view传递给std::istringstream的构造函数。以下代码无法编译(使用Clang v8启用C++17):

std::string_view val = "Hello";
std::istringstream ss(val, std::ios_base::in);

我收到的错误是:
prog.cc:9:24: error: no matching constructor for initialization of 'std::istringstream' (aka 'basic_istringstream<char>')
    std::istringstream ss(val, std::ios_base::in);
                       ^  ~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/clang-6.0.0/include/c++/v1/sstream:651:14: note: candidate constructor not viable: no known conversion from 'std::string_view' (aka 'basic_string_view<char>') to 'const std::__1::basic_istringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >::string_type' (aka 'const basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >') for 1st argument
    explicit basic_istringstream(const string_type& __s,
             ^

然而这个做到了:
std::string_view val = "Hello";
std::istringstream ss(val.data(), std::ios_base::in);

这个问题对我来说很奇怪,因为在这里只应该有一种隐式转换:从 std::string_viewstd::basic_string。但根据错误消息,构造函数正在使用一个 basic_string

为什么我不能直接使用 string_view 而不调用 string_view::data()


1
string_view::data() 这个方法并不正确,因为stringstream 期望的是一个以空字符结尾的字符串。 - user4442671
1
最近我有一种感觉,string_view 只会让事情变得比以前更加复杂和混乱,而以前的事情要么就是 std::string 要么就是 char*。到目前为止,我并不喜欢它。 - void.pointer
1
顺便提一下,在你的“However this does”代码片段中存在未定义行为。构造函数将读取string_view的末尾,寻找空字符。 - Marshall Clow
可以处理std::string_view的另一种输入流在此处找到:https://gist.github.com/andreasxp/ac9adcf8a2b37ac05ff7047f8728b3c7(不是我的作品!) - serbap
3个回答

8
这个问题对我来说很奇怪,因为这里只有1个隐式转换:从std::string_viewstd::basic_stringstring_view无法隐式转换为string。构造函数(好吧,其实是推导指南,但不管怎样)标记为explicit
这应该可以运行(未经测试):
std::string_view val = "Hello";
std::istringstream ss(std::string(val), std::ios_base::in);

明确指定的原因是它是一个(可能)昂贵的操作,涉及内存分配和数据复制。相反的转换(string --> string_view)是廉价的,因此是隐式的。


9
值得注意的是,尽管这种方法“可行”,但这基本上违背了首次使用std::string_view的初衷。一个真正的解决方案将涉及编写自定义的streambuf,如以下链接所示:https://dev59.com/NWcs5IYBdhLWcg3wMhCz#13059195 - user4442671
1
我不同意 - 但在C++20之前。在C++20中,istringstream将具有一个接受string &&并将其移动到流中的构造函数;从而节省了分配/复制。 - Marshall Clow
string_view 不仅仅是 std::string 的替代表示方式。一个简单的例子是,可以用它从一个大文本中提取子字符串而无需复制它。 - user4442671
1
重点是istringstream必须将字符复制到自身中。在C++17(以及之前的版本)中,您需要创建一个字符串(副本),然后再将其复制到流中。在C++20中,该流可以直接从string &&移动构造其内部字符串,从而节省了一次分配和复制。 - Marshall Clow

6
除了其他答案之外,如果使用stringview的意图是为了避免复制char缓冲区,则将其复制到std::string中用于istringstream是不令人满意的。
在这种情况下,您可以使用Boost的iostream作为stringstream的插入式替代品——这避免了复制缓冲区。
#include <boost/iostreams/stream.hpp>

boost::iostreams::stream<boost::iostreams::array_source> stream(buffer, size);

1
这里的问题在于,接受std::string_view参数的std::string构造函数被标记为explicit。这意味着你不能在隐式转换序列中使用它。你需要添加一个强制转换来显式转换它,或者使用std::string/const char[]代替。

1
如果这是显式的,那不是违背了初衷吗?这基本上意味着你需要在每个地方都使用 std::string(val)。我认为隐式转换对于 std::stringstd::string_view 之间的优雅互操作至关重要。他们是否试图使更“浪费”的转换变得明确,以避免意外损害性能? - void.pointer
2
@void.pointer 没错。创建一个 std::string 可能涉及到内存分配,因此可能是昂贵的,应该仔细考虑。相反的转换是便宜的,输出 string_view 是可以的。 - NathanOliver
是的。 - Marshall Clow

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