现代C++中从固定大小std::span创建std::array的惯用方法
问题
我尝试从固定大小的std::span<uint8_t,N>创建std::array<uint8_t,N>,但发现仅能通过memcpy、std::copy或std::ranges::copy实现,这些方法无法防范因目标数组大小指定错误引发的问题。示例代码如下:
#include <algorithm> #include <array> #include <iostream> #include <span> int main(int argc, char **argv) { constexpr size_t N = 10; std::array<uint8_t, N> original; std::span span(original); // of type std::span<uint8,N> std::array copy1(span); // does not work std::array<uint8_t, N> copy2(span); // does not work std::array<uint8_t, N> copy3(begin(span), end(span)); // does not work // ugly stuff that works, but does not protect me if I specify wrong array size constexpr size_t M{N - 1}; //oops, leads to array overflow std::array<uint8_t, M> copy4; std::copy(begin(span), end(span), copy4.begin()); std::ranges::copy(span, copy4.begin()); return 0; }
请问在现代C++中,完成此操作的惯用方法是什么?
惯用解决方案
核心是借助编译期类型检查确保std::span与目标std::array的大小严格匹配,从根源避免手动指定大小出错的问题。以下是几种常用方案:
方案1:自定义类型推导的辅助函数
通过模板函数自动提取std::span的元素类型和固定大小,返回对应尺寸的std::array,编译阶段直接拦截大小不匹配的错误:
#include <algorithm> #include <array> #include <span> #include <cstdint> template<typename T, size_t N> constexpr std::array<T, N> to_array(std::span<T, N> s) { std::array<T, N> arr{}; std::ranges::copy(s, arr.begin()); return arr; } int main() { constexpr size_t N = 10; std::array<uint8_t, N> original{}; std::span<uint8_t, N> span(original); // 自动推导类型和大小,编译期安全 auto copy = to_array(span); // 若尝试传入大小不匹配的span,编译直接报错 // std::span<uint8_t, 5> wrong_span; // auto bad_copy = to_array(wrong_span); // 编译错误 return 0; }
方案2:基于std::to_array的适配(C++20+)
标准库的std::to_array支持从原始数组创建std::array,结合std::span的data()和size()成员,可实现编译期安全的转换:
#include <array> #include <span> #include <cstdint> int main() { constexpr size_t N = 10; std::array<uint8_t, N> original{}; std::span<uint8_t, N> span(original); // 利用span的底层数据和大小创建array,编译期检查尺寸匹配 auto copy = std::to_array<uint8_t>(span.data(), span.size()); // 若手动指定错误的数组大小,编译报错 // auto bad_copy = std::to_array<uint8_t, 5>(span.data(), span.size()); // 编译错误 return 0; }
方案3:带编译断言的强检查封装
如果需要更明确的编译期提示,可以在辅助函数中加入static_assert强化大小匹配检查:
template<typename T, size_t N> constexpr std::array<T, N> span_to_array(std::span<T, N> s) { static_assert(s.size() == N, "Span size must exactly match target array size"); std::array<T, N> arr{}; std::ranges::copy(s, arr.begin()); return arr; }
以上方案均能在编译阶段发现大小不匹配的问题,彻底避免运行时数组溢出风险,符合现代C++类型安全、编译期检查的设计理念。
内容的提问来源于stack exchange,提问作者phinz
相关产品推荐
相关产品推荐

