c++c++20substrstd-span

adding member function substr for std::span<T> which imitates the string_view::substr


In my code, I would like to create a new class named Span by extending the std::span class, but the new Span has some extra member functions which does not exist in std::span.

One is adding the remove_prefix function to std::span, which is implemented and works OK. See my previous question for details: what is the way to remove the first element from a std::span?

Now I would like to add another member function named substr(I try to imitates some member functions of std::string_view because I need to support both std::span<T> and std::string_view in my design). In the old code, I have only std::string_view support, so it has some code snippet like sv.substr(), now I would like sv could be general, which means it could be either std::string_view or std::span<T>(Span<T>).

Here is the minimal sample code I used to solve this issue:

#include <iostream>
#include <span>

using namespace std;

// Span class which is derived class from std::span
// add remove_prefix and substr function which is similar like std::string_view
template<typename T>
class Span : public std::span<T>
{
public:
    // Inheriting constructors from std::span
    using std::span<T>::span;

    // add a public function which is similar to std::string_view::remove_prefix
    constexpr void remove_prefix(std::size_t n) {
        auto& self = static_cast<std::span<T>&>(*this);
        self = self.subspan(n);
    }

    // add another public function substr, which is the span::subspan
    constexpr Span substr(std::size_t pos, std::size_t count){
        auto& self = static_cast<std::span<T>&>(*this);
        auto ret = self.subspan(pos, count);
        return static_cast<Span<T>&>(ret);
    }
};

float arr[3] = {1.0, 2.0, 3.0};

int main()
{
    Span<float> a(arr, 2);
    Span<float> b = a.substr(0, 1);
    return 0;
}

The above code builds and runs OK under g++ with C++20 support. My question is: is my implementation correct? Because I use so many static_cast in the code. In-fact, std::span already has a function named subspan, what I do is just make a function name alias substr.

Any ideas? Thanks.


Solution

  • Your implementation of remove_prefix isn't faulty, but it can be simplified to

    constexpr void remove_prefix(std::size_t n) {
        *this = subspan(n);
    }
    

    Your implementation of substr is faulty, because you are static_casting an lvalue reference to an object who's most derived type is std::span<T>, not Span<T>. You can fix it:

    constexpr Span substr(std::size_t pos, std::size_t count){
        return subspan(pos, count);
    }
    

    However, I wouldn't do this. I would instead change the calling code to use free functions that you've overloaded for std::span<T> and std::string_view (et. al.)

    template<typename T>
    constexpr void remove_prefix(std::span<T> & span, std::size_t n) {
        span = span.subspan(n);
    }
    
    template<typename CharT, typename Traits>
    constexpr void remove_prefix(std::basic_string_view<CharT, Traits> & view, std::size_t n) {
        view.remove_prefix(n);
    }
    
    template<typename T>
    constexpr std::span<T> substr(std::span<T> span, std::size_t n) {
        return span.subspan(n);
    }
    
    template<typename CharT, typename Traits>
    constexpr std::basic_string_view<CharT, Traits> substr(std::basic_string_view<CharT, Traits> view, std::size_t n) {
        return view.substr(n);
    }