将具有动态范围的 std::span 转换为具有静态范围的 std::span

Hol*_*uss 6 c++ c++20

我曾经使用指南支持库中的 gsl::span 工作了一段时间。使用 gsl::span,可以定义一个采用静态范围并使用动态范围的 gsl::span 调用它的函数,例如

void f(gsl::span<int, 5> s) { }
...
std::vector<int> v(100);
f(gsl::make_span(v).subspan(42, 5));    // Works
Run Code Online (Sandbox Code Playgroud)

将代码移植到 std::span 后,我注意到这不再可能:

void f(std::span<int, 5> s) { }
...
std::vector<int> v(100);
f(std::span(v).subspan(42, 5));    // Does not work
Run Code Online (Sandbox Code Playgroud)

这种差异有什么原因吗?是否有任何推荐的方法可以将具有动态范围的 std::span 转换为具有固定范围的 std::span?当然,在调用函数时可以创建一个固定范围的新跨度:

f(std::span<int, 5>(&v[42], 5));
Run Code Online (Sandbox Code Playgroud)

但是,我认为“subspan”变体的可读性要好得多,它以更好的方式表达了意图,并且允许在调试版本中进行适当的范围检查。

Bar*_*rry 7

这种差异有什么原因吗?

P1976 详细介绍了这一点。Fixed-extent spans 有显式的构造函数来尝试减轻未定义的行为,因此std::span<int>(的类型std::span(v).subspan(42, 5))不能转换为std::span<int, 5>.

是否有任何推荐的方法可以将具有动态范围的 std::span 转换为具有固定范围的 std::span?

如果偏移量和计数都是常量表达式(在这种情况下为真),则可以使用 的其他重载subspan()

f(std::span(v).subspan<42, 5>());
Run Code Online (Sandbox Code Playgroud)

这种重载以它的方式存在,因此您可以安全地获取固定范围跨度的固定范围子跨度,但是当想要获取动态范围跨度的固定范围子跨度时不太理想,理想情况下我们只是有想要的std::span(v).subspan<5>(42)

对于这种情况,您可以改为first()与 结合使用subspan

f(std::span(v).subspan(42).first<5>());
Run Code Online (Sandbox Code Playgroud)

稍微长一点,但完成工作。

或者你可以全力以赴并使用构造函数:

f(std::span<int, 5>(std::span(v).subspan(42, 5)));
f(std::span<int, 5>(v.data() + 42, 5));
Run Code Online (Sandbox Code Playgroud)