std::ranges::copy_n是c++20引入的现代范围算法,要求源为满足range概念的类型(如vector、span),目标为可写迭代器,不检查越界,n需非负且不超过源长度,返回copy_result结构。

std::ranges::copy_n 的基本用法和适用场景
它只在 C++20 及以上可用,且要求源范围支持 begin() 和 end()(比如 std::vector、std::array、原生数组),目标迭代器必须可写。它不检查源是否真有足够元素——越界行为是未定义的,这点和传统 std::copy_n 一致,但接口更现代。
常见错误现象:std::ranges::copy_n 编译失败,通常是因为传入了非范围类型(比如裸指针)或目标不是迭代器;或者运行时崩溃,因为 n 超出源范围长度。
- 源参数必须是 range 概念满足者,不能直接传
int*;若只有指针,得包装成std::span或用std::ranges::subrange -
n是std::iter_difference_t类型(通常是ptrdiff_t),不能为负 - 目标迭代器只需支持
operator++和operator=,不要求是随机访问
如何安全传入原生数组或指针区间
原生数组名本身不是 range,直接传会编译报错:「no matching function for call to copy_n」。必须显式构造一个 view 或 subrange。
推荐做法是用 std::span 包装源,它轻量、零开销,且天然满足 std::ranges::range 概念:
int src[] = {1, 2, 3, 4, 5};
int dst[3];
std::ranges::copy_n(std::span{src}, 3, dst); // ✅ 正确
如果只有两个指针(如 int* first, int* last),可用 std::ranges::subrange:
auto r = std::ranges::subrange(first, last); std::ranges::copy_n(r, 3, dst);
- 别用
std::ranges::make_subrange(已弃用) - 避免写
std::ranges::copy_n({first, last}, ...)—— 初始化列表不构成合法 range - 若源长度未知,务必先确认
std::distance(first, last) >= n,否则 UB
与传统 std::copy_n 的关键差异和迁移注意点
函数签名不同:旧版接受两个迭代器 + n;新版接受 range + n + 目标迭代器。这意味着你不能再把源“拆成两个迭代器”传进去。
性能上无差异,底层仍调用最优 memcpy 或循环拷贝,但语义更清晰——强调“从一个整体中取前 n 个”。兼容性方面,MSVC 19.30+、GCC 10.2+、Clang 13+ 支持完整特性;早期版本可能缺失 std::ranges::copy_n 声明(需开启 -std=c++20 且确保标准库实现完整)。
- 头文件仍是
<algorithm></algorithm>,无需额外 include - 若项目需兼容 C++17,不能用此函数,应回退到
std::copy_n+ 手动构造迭代器对 - 返回值类型不同:新版返回
std::ranges::copy_result(含in和out成员),旧版返回单个迭代器
容易被忽略的边界情况:空 range 和 n == 0
n == 0 是合法的,函数立即返回,不操作任何元素,目标迭代器也不前进。空 range(如空 std::vector)传入时,只要 n == 0 就安全;若 n > 0 则触发未定义行为——哪怕 range 为空,也不会抛异常或断言。
- 没有内置长度校验,所有长度检查必须由调用方负责
- 调试时建议加断言:
assert(std::ranges::size(src) >= n)(仅限 debug build) - 若源是输入范围(如
std::istream_view),std::ranges::size不可用,此时只能靠业务逻辑保证 n 合理
真正麻烦的是那种动态生成的 range,比如 filter view 或 transform view,它们的 size 可能不可知,这时 copy_n 的风险比显式迭代更高——你得自己确保不会提前耗尽。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











