You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为std::span扩展仿std::string_view的substr成员函数,请教实现正确性

关于继承std::span实现Span类的问题解答

你的实现存在不安全的地方,虽然当前能编译运行,但static_cast的使用有未定义行为风险,下面具体分析并给出修正方案:

1. substr函数的错误点

你在substr里用static_cast<Span<T>&>(ret)转换std::span<T>对象为Span<T>引用,这属于向下转型(基类转派生类),但ret本身并不是Span<T>的实例,这种转型是未定义行为——当前编译器没报错只是巧合,一旦后续Span类添加成员变量,这种转型会直接访问非法内存,引发崩溃或奇怪的运行问题。

正确的写法应该利用你已经继承的基类构造函数,直接把subspan返回的std::span<T>转换成Span<T>:

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

因为你已经通过using std::span<T>::span;继承了std::span的所有构造函数,而std::span支持用另一个std::span来构造,所以这种写法安全且符合C++规范。

2. remove_prefix函数的简化

你写的remove_prefix里的static_cast完全没必要,派生类可以直接访问基类的成员,简化后代码更清晰:

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

派生类对象本身可以直接被赋值为基类的std::span对象,不需要额外转成基类引用。

3. 继承std::span的注意事项

std::span是非多态类(没有虚函数表),继承它来扩展成员函数是可行的,但要注意:

  • 不要用std::span<T>*或std::span<T>&指向Span<T>对象后执行删除操作(不过span是轻量级视图对象,一般不会这么用)
  • 所有扩展的成员函数都要基于基类的合法操作实现,你的remove_prefix和substr都是用subspan实现,这一点是安全的。

4. 统一接口的替代方案

如果你只是想让std::span和std::string_view拥有统一的substr接口,也可以不用继承,而是写全局模板重载函数:

#include <span>
#include <string_view>

template<typename T>
constexpr auto substr(const std::span<T>& s, std::size_t pos, std::size_t count) {
    return s.subspan(pos, count);
}

constexpr std::string_view substr(const std::string_view& sv, std::size_t pos, std::size_t count) {
    return sv.substr(pos, count);
}

旧代码里的sv.substr(pos, count)改成substr(sv, pos, count),就能同时支持两种类型,避免了继承带来的潜在问题。


内容的提问来源于stack exchange,提问作者ollydbg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:42:31