Search code examples
c++string-view

Does using string_view lead to an unnecessary string copy in this scenario?


What I'm trying to do is have my class accept a string during construction. I read that string_view was a replacement for const string& so naturally I wrote a constructor like this. This allows me to accept c++ and c strings.

Url::Url(boost::string_view raw_url)
    : url_(static_cast<std::string>(raw_url)) {

The problem which might be here is that when an rvalue is passed, there is a unnecessary copy instead of a move. Is the solution to make another constructor which takes string&&? What is the best practice here?


Solution

  • Note: I'll write the answer considering std::string_view, but boost::string_view should be similar.


    I read that string_view was a replacement for const string&

    It is useful to understand the reason why std::string_view was added in the first place.

    What is the problem with const string&?

    It is certainly efficient if you pass an std::string object and you don't gain anything with std::string_view. But suppose now that you are passing a char* containing a large string. In that case a temporary std::string object will be created (and the whole string referenced by that char* will be copied) just so the function receives an std::string. That is when std::string_view shines. If you pass a char* or a std::string (or anything that can be converted to std::string_view) to a function accepting std::string_view (by value, no need to accept a std::string_view by reference) then the new std::string_view object that is created is very cheap, since it is only "a view" and it does not copy the underlying string.


    But your case is different. Since you are copying the string anyway, then your function should just accept a string by value and move the string inside the function. Something such as

    Url::Url(std::string raw_url)
        : url_(std::move(raw_url)) {
    

    There is even a clang-tidy warning to tell you this.

    The advantage is that if the user of your function pass an lvalue you make a copy (you need it anyway) and a move, but if they pass an rvalue then there is no copy and only a move.