Reputation: 7717
There is an implicit conversion from std::string
to std::string_view
and it's not considered unsafe, even though this surely may cause a lot of dangling references if the programmer is not careful.
On the other hand, there's no implicit conversion from std::string_view
to std::string
using same argument but in the completely opposite fashion: because the programmer may not be careful.
It's lovely that C++ has a replacement for a raw const char*
pointer, while making it super confusing and stripped to the bone:
const char*
-> std::string
: OKstd::string_view
-> std::string
: NOPEstd::string
= const char*
: OKstd::string
= std::string_view
: OKstd::string
+= const char*
: OKstd::string
+= std::string_view
: OKconst char*
+ std::string
: OKstd::string_view
+ std::string
: NOPEstd::string
+ const char*
: OKstd::string
+ std::string_view
: NOPEAm I missing something or is this a total nonsense?
In the end, how useful is this string view without all the crucial pieces that make it similar to const char*
? What's the point of integrating it into the ecosystem of stdlib while not making the last step to make it complete? After all, if we need an object that represents a piece of a string we could write our own. Actually, a lot of libraries already have done that, years ago. The whole point of making something standard is to make it useful for widest range of use cases, isn't it?
Are they going to fix this in C++23?
Upvotes: 150
Views: 101198
Reputation: 957
One of the reasons string views are useful in embedded environment is precisely because they don't do dynamic allocation and we get safety due to the length being passed as part of the view. So for me the lack of an implicit cast to std::string is not a deal breaker because that would require dynamic allocation. A silent conversion would be an error in this environment and have to be found and removed.
Upvotes: 5
Reputation: 131525
You only listed one implicit conversion out of your entire set of examples: const char*
-> std::string
; and you're asking for another one. In all of the other 'OK'-marked functions - allocations of memory are obvious/explicit:
Implicit conversions in general have benefits and detriments. C++ is actually mostly stingy with these, and inherits most implicit conversions from C. But - const char*
to std::string
is an exception. As @ArthurTacca notes, it allocates memory. Now, the
C++ core guidelines say:
C.164: Avoid implicit conversion operators
Reason:
Implicit conversions can be essential (e.g., double to int) but often cause surprises (e.g., String to C-style string).
and this is doubly the case when the unintended conversion performs expensive operations like allocation, which have side effects, and may make system calls.
PS - std::string_view
has quite a few gotchas; see this CppCon 2018 talk by Victor Ciura:
Enough string_view
to hang ourselves with
So remember it's not some kind of universal panacea; it's another class that you need to use with care, not carelessness.
There actually does exist an explicit constructor of std::string
from std::string_view
. Use it - by passing std::string{my_string_view}
to a function taking a string.
Upvotes: 26
Reputation: 9988
The problem is that std::string_view
-> std::string
makes a copy of the underlying memory, complete with heap allocation, whereas the implicit std::string
-> std::string_view
does not. If you've bothered to use a std::string_view
in the first place then you obviously care about copies, so you don't want one to happen implicitly.
Consider this example:
void foo1(const std::string& x)
{
foo2(x);
}
void foo2(std::string_view x)
{
foo3(x);
}
void foo3(const std::string& x)
{
// Use x...
}
The function foo2
could've used a const std::string&
parameter, but used a std::string_view
so that it is more efficient if you pass in a string that isn't a std::string
; no surprises there. But it's less efficient than if you'd just given it a const std::string&
parameter!
foo2
is called with a std::string
argument (e.g. by foo1
): When foo2
calls foo3
, it creates a copy of the string. If it had a const std::string&
argument, it could've used the object it already had.foo2
is called with a const char*
argument: A std::string
copy has to be made sooner or later; with a const std::string&
parameter it gets made earlier, but overall there's exactly one copy either way.Now imagine foo2
calls multiple functions like foo3
, or calls foo3
in a loop; it's making exactly the same std::string
object over and over. You'd want the compiler to notify you about this.
Upvotes: 68