Compiling with gcc-7.1 with the flag -std=c++17
, the following program raises an error:
#include <string_view> void foo(const char* cstr) {} void bar(std::string_view str){ foo(str); }
The error message is
In function 'void bar(std::string_view)': error: cannot convert 'std::string_view {aka std::basic_string_view<char>}' to 'const char*' for argument '1' to 'void foo(const char*)' foo(str);
I'm surprised there is no conversion to const char*
because other libraries (abseil, bde), provide similar string_view
classes which implicitly convert to const char*
.
string is an object meant to hold textual data (a string), and char* is a pointer to a block of memory that is meant to hold textual data (a string). A string "knows" its length, but a char* is just a pointer (to an array of characters) -- it has no length information.
What is string_view ? Conceptually, string_view is only a view of the string and cannot be used to modify the actual string. When a string_view is created, there's no need to copy the data (unlike when you create a copy of a string).
The std::string_view, from the C++17 standard, is a read-only non-owning reference to a char sequence. The motivation behind std::string_view is that it is quite common for functions to require a read-only reference to an std::string-like object where the exact type of the object does not matter.
A std::string_view
doesn't provide a conversion to a const char*
because it doesn't store a null-terminated string. It stores a pointer to the first element, and the length of the string, basically. That means that you cannot pass it to a function expecting a null-terminated string, like foo
(how else are you going to get the size?) that expects a const char*
, and so it was decided that it wasn't worth it.
If you know for sure that you have a null-terminated string in your view, you can use std::string_view::data
.
If you're not you should reconsider whether using a std::string_view
in the first place is a good idea, since if you want a guaranteed null-terminated string std::string
is what you want. For a one-liner you can use std::string(object).data()
(note: the return value points to a temporary std::string
instance that will get destroyed after the end of the expression!).
Simply do a std::string(string_view_object).c_str()
to get a guaranteed null-terminated temporary copy (and clean it up at the end of the line).
This is required because string view doesn't guarantee null termination. You can have a view into the middle of a longer buffer, for example.
If this use case is expensive and you have proven it to be a bottleneck, you can write an augmented string_view
that tracks if it is null terminated (basically, if it was constructed from a raw char const*
).
Then you can write a helper type that takes this augmented string_view
and either copies it to a std::string
or stores the augmented string_view
directly, and has an implicit cast-to-char const*
that returns the properly null-terminated buffer.
Then use that augmented helper type everywhere in your code base instead of string_view
, possibly augmenting string view interaction with std string as well to catch the cases where you have a view that goes to the end of the std string buffer.
But really, that is probably overkill.
A better approach is probably rewriting the APIs that take const char*
to take string_view
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With