Search code examples
c++c++20string-view

Constructing a string_view from a range of chars


While a span can be constructed from a range, a string_view cannot be constructed from a range of chars.

Thus, for example, the following code is required:

// assume chars_span is a span of chars
std::cout << std::string_view(chars_span.data(), chars_span.size());
// or:
std::cout << std::string_view(chars_span.begin(), chars_span.end());

Instead of a simpler range-syntax that is not supported:

std::cout << std::string_view(chars_span);

Is there a reason for not having a constructor for string_view that accepts a range of chars, or was it just overlooked or not considered important enough?


Solution

  • P1391r3 proposed this, though this was dropped in the version that was eventually adopted for C++20: P1391r4. The reason for the drop unfortunately is completely absent from the paper (indeed, the paper does not even mentioned that it was dropped).

    However, a follow-up paper, P1989R0 presents the issue as what happens if we had a type like this (I modified the example slightly):

    struct buffer {
        buffer() {};
        char const* begin() const { return data; }
        char const* end() const { return data + 42; }
        operator string_view() const {
            return string_view(data, data + 2);
        }
    private:
        char data[42];
    };
    

    Here, buffer is convertible to string_view. But the way in which it is convertible to string_view differs in the way in which string_view's range constructor would do it (the former gives you two chars, the latter gives you 42). As far as I am aware, nobody has actually pointed out the existence of such types.

    Nevertheless, the direction was to ensure that those types continue to just work, so the new paper has a more complicated set of constraints for that particular constructor.


    A more interesting example would be something like:

    using ci_string = std::basic_string<char, case_insensitive_traits>;
    
    ci_string value = "Hello";
    std::string_view sv = value;
    

    Any kind of straightforward range-based reasoning would allow the conversion from ci_string to std::string. A ci_string is a perfectly good contiguous range of char, without any weird conversion issues like the buffer type from earlier. But while ci_string should be convertible to basic_string_view<char, case_insensitive_traits>, we probably wouldn't want to avoid it being convertible to just normal string_view. That's unlikely to be intended, so it's something that we need to try to guard against.

    This case is much more motivating for me than the buffer case.