基于std::list的boost::iterator_range接入ranges视图的合法性与标准适配
GCC与Clang在C++20下的Range概念兼容性问题分析
问题场景
以下代码在C++20标准下用GCC编译失败,但Clang可正常编译。执行任一修改即可让GCC通过编译:
- 将
std::list改为std::vector - 将
r声明为const - 切换至C++17标准
原代码:
#include <boost/range/iterator_range.hpp> #include <functional> #include <list> #include <range/v3/range/conversion.hpp> #include <range/v3/view/transform.hpp> #include <vector> using namespace ranges; using namespace ranges::views; int main() { std::list<int> v; auto r = boost::make_iterator_range(v); auto w = r | transform([](auto x){ return x; }) | to_vector; }
简化复现示例
问题可简化为以下代码:
#include <boost/range/iterator_range_core.hpp> #include <list> #include <range/v3/view/ref.hpp> int main() { std::list<int> l; using Foo = const ranges::ref_view<boost::iterator_range<decltype(l.begin())>>&; ranges::_size_::has_non_member_size<Foo>; }
该示例的编译表现:
- C++20下:GCC <=10.5可编译,GCC >=11.1编译失败,错误提示概念自依赖
- C++17下:所有GCC版本均可编译
- 修复方式:移除
Foo中的const,或改用std::vector
标准合规性与原因解析
1. 谁的行为符合C++标准?
根据C20标准,Clang的行为是正确的,GCC在C20下的报错属于实现缺陷。
2. 核心原因拆解
问题根源在于Range-v3库的has_non_member_size概念检查逻辑,以及GCC对C++20概念中自依赖类型推导的处理差异:
- 当处理
const ranges::ref_view<boost::iterator_range<...>>&这类类型时,GCC >=11.1错误判定存在概念自依赖(即概念检查过程中递归依赖自身的实例化结果),但这种依赖是C++20标准允许的合法场景。 std::list与std::vector的差异:std::vector的迭代器范围自带成员size(),而std::list的迭代器范围没有,这会触发Range-v3去检查非成员size()函数的存在性,进而暴露GCC的概念处理缺陷。- 将
r声明为const后,boost::iterator_range的const版本会改变类型推导路径,避开了触发GCC错误的代码分支。 - C++17没有概念特性,Range-v3用SFINAE替代概念检查,SFINAE的规则与概念实例化逻辑不同,因此GCC不会报错。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

