为何在模板函数中使用std::span需要显式构造?
问题:调用std::span参数的模板函数时必须显式构造?
我编写了如下函数:
#include <algorithm> #include <iostream> #include <numeric> #include <ranges> #include <span> #include <type_traits> template <typename T> concept arithmetic = std::is_arithmetic_v<T>; template <arithmetic T, std::size_t N> auto sum(std::span<T, N> const a, std::span<T, N> const b, std::span<T, N> const c) { std::ranges::transform(a, b, std::begin(c), [](auto const a, auto const b) { return a + b; }); }
当我尝试以下面的方式调用该函数时:
std::array<int, 10> a{}; std::array<int, 10> b{}; std::array<int, 10> c{}; // 对a、b、c进行更多初始化 // ... sum(a, b, c);
收到了如下模板推导错误:
<source>:41:5: error: no matching function for call to 'sum' sum(a, b, c); ^~~ <source>:15:10: note: candidate template ignored: could not match 'span' against 'array' auto sum(span_const_of_const<T, N> a, ^
我必须通过显式构造std::span来修复调用:
sum(std::span(a), std::span(b), std::span(c));
我原本以为使用std::span不需要这么做,请问我哪里出错了?
原因分析
模板参数推导是精确匹配的过程,编译器不会自动将std::array隐式转换为std::span来完成推导。你的sum函数模板要求参数是std::span<T, N>,但传入的std::array<int,10>是完全不同的类型,编译器无法推导出模板参数T和N,因此报错。
解决方案
有几种方式可以优化调用体验,同时保留你需要的功能:
1. 修改函数模板,接受可转换为span的类型
让函数参数接受任意可以转换为固定大小std::span的类型,用约束确保类型合法:
template <arithmetic T, std::size_t N> auto sum(const auto& a, const auto& b, const auto& c) requires (std::convertible_to<const decltype(a)&, std::span<const T, N>> && std::convertible_to<const decltype(b)&, std::span<const T, N>> && std::convertible_to<const decltype(c)&, std::span<T, N>>) { std::ranges::transform(std::span<const T, N>(a), std::span<const T, N>(b), std::begin(c), [](auto const a, auto const b) { return a + b; }); }
这样调用时直接传std::array即可,编译器会自动验证类型并转换为span。
2. 使用非模板的span参数(牺牲固定大小检查)
将函数参数改为非模板的std::span,利用CTAD(类模板参数推导)完成隐式转换:
template <arithmetic T> auto sum(std::span<const T> a, std::span<const T> b, std::span<T> c) { std::ranges::transform(a, b, std::begin(c), [](auto const a, auto const b) { return a + b; }); }
这种方式可以直接传std::array调用,但会丢失编译期固定大小的检查,允许传入不同长度的span。
3. 保留原函数,显式构造span(你当前的做法)
继续使用显式构造的方式,这种方式的优势是保留编译期固定大小的安全检查,确保传入的三个容器长度完全一致,避免运行时错误。
总结
核心问题在于模板推导不支持隐式转换,所以直接传std::array无法匹配std::span<T,N>的模板参数。根据需求选择合适的方案:如果需要编译期固定大小检查,要么显式构造span,要么修改函数模板接受可转换类型;如果不需要固定大小检查,可以改用非模板的span参数。
内容的提问来源于stack exchange,提问作者SRSR333
相关产品推荐
相关产品推荐

