为何新版编译器允许将非viewable_range的右值范围传给部分范围适配器?
为什么新版C++编译器允许右值范围传给部分范围适配器?
核心原因是C++标准通过P2415R2提案扩展了范围适配器的能力,让它们可以接受可移动的右值范围,并自动将其包装为std::ranges::owning_view,从而满足viewable_range的要求。
1. 原C++20标准的限制
最初的C++20标准规定,范围适配器的第一个参数必须是viewable_range,即:
- 要么是
view(轻量、可复制的范围类型); - 要么是
borrowed_range(迭代器不会悬空的范围,比如左值容器)。
右值容器(如std::vector{1,2,3})既不是view,也不属于borrowed_range,所以旧版编译器会拒绝将其传给范围适配器。
2. P2415R2带来的变化
为了提升易用性,P2415R2给范围适配器新增了重载:当传入可移动的右值范围时,适配器会自动把它包装成owning_view——这是一种持有范围所有权的view,自然符合viewable_range的定义。clang 16.0.0、gcc 12.0及以上版本实现了这个提案,因此允许这类代码编译通过。
3. 你的测试案例解析
std::views::drop(std::vector{1,2,3,4,5}, 2):新版编译器将右值vector包装为owning_view后再应用drop,最终生成合法的view;旧版编译器未实现该提案,因此报错。- 修正后的
std::views::transform(...):同样,新版编译器自动处理右值vector,包装为owning_view后适配transform,所以编译通过;你最初的笔误代码(传2而非lambda)本身不符合transform的参数要求,因此无论编译器版本都无法编译。
4. 本质没有打破规则
这类代码并没有违反viewable_range的定义——适配器实际接收的是包装后的owning_view(属于view),仍然满足viewable_range的要求。只要编译器实现了P2415R2,大部分标准范围适配器(如filter、take等)都支持这种右值范围的自动转换。
内容的提问来源于stack exchange,提问作者Waker
相关产品推荐
相关产品推荐

