c++ 为std::span添加成员函数substr< T>,该函数模拟string_view::substr

46qrfjad  于 2023-02-26  发布在  其他
关注(0)|答案(2)|浏览(149)

在我的代码中,我想通过扩展std::span类来创建一个名为Span的新类,但是新的Span有一些额外的成员函数,而std::span中不存在这些函数。
一种是在std::span中添加remove_prefix函数,该函数已经实现,工作正常,详见我前面的问题:what is the way to remove the first element from a std::span?
现在我想添加另一个名为substr的成员函数(我试着模仿std::string_view的一些成员函数,因为我需要在我的设计中同时支持std::span<T>std::string_view)。在旧代码中,我只有std::string_view支持,所以它有一些代码片段像sv.substr(),现在我希望sv可以是通用的,这意味着它可能是std::string_viewstd::span<T>Span<T>)。
下面是我用来解决这个问题的最小示例代码:

#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;
}

上面的代码在g 下构建并运行正常,并支持C20。我的问题是:我的实现是否正确?因为我在代码中使用了太多的static_cast。事实上,std::span已经有一个名为subspan的函数,我所做的只是使函数名别名为substr
有什么主意吗?谢谢。

cqoc49vn

cqoc49vn1#

您的remove_prefix实现没有错,但是可以简化为

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

substr的实现是错误的,因为您正在静态转换一个左值引用到一个对象,该对象的最大派生类型是std::span<T>,而不是Span<T>

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

但是,我不会这样做,而是将调用代码改为使用为std::span<T>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);
}
4dbbbstv

4dbbbstv2#

谢谢你的提问!
所以,我认为你应该考虑改变你的设计,而不是改变STL(:
首先,从std::span派生是不安全的,因为它没有虚析构函数,所以有可能泄漏内存:Is it okay to inherit implementation from STL containers, rather than delegate?
其次,它们根本不是虚拟的,这意味着继承被错误地使用了,因为,在我看来,它的目的是创建要重写的接口,以便模块的细节可以相互隐藏。
很难说如果不了解您的设计,我会怎么做,但我建议您使用以下解决方案:

  • 使用多态性或显式模板专用化(使用constexpr if)为两个类定义方法;
  • 为你的具体设计(解决你的最终目标)创建一个界面,隐藏包含其中一个视图的细节;
  • 您可以从std::string_view构造std::span<const char>,并且只使用其中一个。

相关问题