c++ - 扩展 std::span 时模板参数推导的问题
问题描述
我正在尝试扩展std::span
以检查边界operator[]
(我知道gsl::span
提供了这个)
我已将我的容器声明如下:
#include <span>
#include <string>
#include <utility>
#include <stdexcept>
template <typename ... TopArgs> class BoundsSpan : private std::span<TopArgs...> {
public:
typename std::span<TopArgs...>::reference operator[](std::size_t idx) const {
if (idx >= this->size()) [[unlikely]] {
throw std::out_of_range(std::string("span out of bounds access detected - wanted index [" + std::to_string(idx) + "] but size is " +
std::to_string(this->size())));
}
return std::span<TopArgs...>::operator[](idx);
}
template<typename ... Args>
BoundsSpan(Args&& ... args) : std::span<TopArgs...>(std::forward<Args>(args) ...) {}
};
这似乎很好用,但是我注意到模板参数推导不再有效。例如
int main(int argc, char *argv[]) {
BoundsSpan span(argv, argc);
}
给出“类模板'span'的模板参数太少”并需要显式BoundsSpan<char*>
代替 - 这不是常规的情况std::span
此外,BoundsSpan
在具有运行时长度的 C 样式数组上构造 a 会给出“不能将可变修改的类型用作模板参数”——我是否在这里忽略了模板特化?一个示例复制器是
void func(int len) {
int arr[len];
BoundsSpan<int> span(arr, len);
}
解决方案
std::span 模板参数列表与您的可变参数列表不兼容。它需要:
template <typename T, std::size_t N>
class span {...};
但是,您的课程是根据可变参数类型列表工作的。当需要非类型模板参数时,您无法将模板参数“向上”传播到跨度基础,并且您只能传递类型。最好尝试匹配您正在模拟的跨度的模板签名。这为问题带来了一些简化:
您可以将所有跨度构造函数公开为您自己的(即
using std::span<T, N>::span;
)您可以使用与 std::span 相同的演绎指南,为您的班级重命名。
这是您的代码的修订版本,我发现它可以正常工作:
template <class T, std::size_t N = std::dynamic_extent>
class BoundsSpan : private std::span<T, N> {
public:
using std::span<T, N>::span;
using std::span<T, N>::data;
using std::span<T, N>::size;
// .. and all the other interface functions
typename std::span<T, N>::reference operator[](std::size_t idx) const {
if (idx >= this->size()) [[unlikely]] {
throw std::out_of_range("span out of bounds - wanted index [" +
std::to_string(idx) + "] but size is " + std::to_string(size()));
}
return std::span<T, N>::operator[](idx);
}
};
然后使用 std::span 的推理指南来获得灵感:
template <class It, class EndOrSize>
BoundsSpan(It, EndOrSize) -> BoundsSpan<std::remove_reference_t<std::iter_reference_t<It>>>;
template<class T, std::size_t N>
BoundsSpan(T (&)[N]) -> BoundsSpan<T, N>;
template<class T, std::size_t N>
BoundsSpan(std::array<T, N>&) -> BoundsSpan<T, N>;
template<class T, std::size_t N>
BoundsSpan(const std::array<T, N>&) -> BoundsSpan<const T, N>;
template<class R>
BoundsSpan(R&&) ->
BoundsSpan<std::remove_reference_t<std::ranges::range_reference_t<R>>>;
有了这个,它应该可以在你的例子中工作,就像跨度一样。您可能还想包装const
operator[] 的版本。
推荐阅读
- reactjs - React 允许某些 url 的服务器端渲染
- assembly - 这些代码行在 x86 汇编中意味着什么?
- javascript - 使用 Javascript 和 Canvas 重复绘图
- django - 无效的 HTTP_HOST 标头:提供的域名无效——使用容器名称向 dockerized django 应用程序请求
- .net - 如何使用 ImageResizer 插件添加 Access-Control-Allow-Origin 标头
- azure - Power bi 服务在 Azure 中不可用
- r - 将文件从 Sharepoint 导入到 R
- jquery - Jquery 日期选择器没有在文本字段的焦点上触发
- node.js - 我无法处理猫鼬中的填充
- javascript - 让 Protractor 轮询承诺,直到满足条件