7

I'm not able to provide a std::string_view to std::istringstream's constructor. The following code doesn't compile (C++17 enabled using Clang v8):

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

The error I get is:

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,
             ^

However this does:

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

This issue is weird to me because there's only 1 implicit conversion that should be happening here: std::string_view to std::basic_string. The constructor is taking a basic_string according to the error message.

Why can't I use string_view verbatim here without calling string_view::data()?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 1
    `string_view::data()` is not even right, as the stringstream will be expecting a null-terminated string. –  Jul 29 '19 at 19:08
  • 1
    I've been getting the feeling lately that `string_view` just makes things more complicated & confusing than before, where things were either `std::string` or `char*`. So far I'm not enjoying it. – void.pointer Jul 29 '19 at 19:11
  • 1
    btw, you've got UB in your "However this does" code snippet. The constructor will read off the end of the `string_view`, looking for a null char. – Marshall Clow Jul 29 '19 at 19:23
  • An alternative input stream that can handle std::string_view can be found here: https://gist.github.com/andreasxp/ac9adcf8a2b37ac05ff7047f8728b3c7 (not my work!) – serbap Sep 08 '21 at 06:11

3 Answers3

8

This issue is weird to me because there's only 1 implicit conversion that should be happening here: std::string_view to std::basic_string.

A string_view is not implicitly convertible to a string. The constructor (ok, deduction guide, but whatever) is marked as explicit.

This should work (untested):

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

The reason for the explicit is that it's a (potentially) expensive operation; involving memory allocation and copying of the data. The opposite conversion (string --> string_view) is cheap, and as such is implicit.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • 8
    it's worth noting that while this "works", it pretty much defeats the purpose of using `std::string_view` in the first place. A real answer would involve writing a custom streambuf like so: https://stackoverflow.com/a/13059195/4442671 –  Jul 29 '19 at 19:52
  • 1
    I disagree - but not before C++20. In C++20, `istringstream` will have a constructor that takes a `string &&` and moves it into the stream; thus saving an allocation/copy. – Marshall Clow Jul 29 '19 at 19:58
  • `string_view` is used for much more than an alternative representation of a `std::string`. A simple example would be for extracting a substring out of a large text without having to make a copy of it. –  Jul 29 '19 at 20:06
  • 1
    The point is that `istringstream` has to copy characters into itself. In C++17 (and before), you have to make a string (copy) and then copy them again into the stream. In C++20, the stream can just move-construct it's internal string from the `string &&`, saving an allocation and a copy. – Marshall Clow Jul 29 '19 at 20:41
6

In addition to the other answers, if the intention to use stringview is to avoid copies of a char buffer, then copying it into std::string for istringstream is not satisfactory.

In that case, you can use Boost's iostream as a drop-in replacement for stringstream - this avoids copying the buffer.

#include <boost/iostreams/stream.hpp>

boost::iostreams::stream<boost::iostreams::array_source> stream(buffer, size);
Riot
  • 15,723
  • 4
  • 60
  • 67
1

The issue here is that that std::string constructor that takes a std::string_view is marked explicit. This means you can't use it in an implicit conversion sequence.

You'll either need to add a cast to explicitly convert it, or use a std::string/const char[] instead.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    If it's explicit, doesn't that defeat the purpose? That basically means you need to do `std::string(val)` everywhere. My thought on the matter was that implicit conversions would be key to elegant interoperability between `std::string` and `std::string_view`. Are they trying to make the more "wasteful" conversion be explicit as to avoid accidentally hurting performance? – void.pointer Jul 29 '19 at 19:14
  • 2
    @void.pointer Exactly. Making a `std::string` could involve an allocation so it is potentially expensive and should be carefully considered. The opposite conversion though is cheap, and spitting out `string_view`'s is OK. – NathanOliver Jul 29 '19 at 19:15
  • Yes. (SO doesn't like short answers) – Marshall Clow Jul 29 '19 at 19:16