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.
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);
}