C++20如何判断类型是否为Range?实现递归打印嵌套Range
递归打印嵌套Range的实现问题及解决方法
问题背景
需要实现一个递归打印嵌套std::ranges::range的函数print,将Range内容输出到std::cout,原尝试的代码在GCC12中触发内部编译器错误:
原代码:
void print(ranges::range auto &&range) { if (std::is_convertible_v<ranges::range_value_t<decltype(range)>,ranges::range auto>) // or if constexpr(...) or std::is_same_v ??? { for (auto &&subrange : range) { std::cout << "{ "; print(subrange); std::cout << "} "; } } else { std::cout << "{"; for (auto &&i : range) { std::cout << i << ", "; } std::cout << "} "; } }
触发的编译器错误:
internal compiler error: Segmentation fault [build] 90 | if (std::is_convertible_v<ranges::range_value_t<decltype(range)>,ranges::range auto>) [build] | ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [build] 0x7f11b04f8cef ??? [build] ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 [build] 0x7f11b04e050f __libc_start_call_main [build] ../sysdeps/nptl/libc_start_call_main.h:58 [build] 0x7f11b04e05c8 __libc_start_main_impl [build] ../csu/libc-start.c:381
错误原因
原代码中试图用std::is_convertible_v结合ranges::range auto判断元素类型是否为Range,这是错误的用法:ranges::range auto是**概念(Concept)**的简写形式,不能作为模板参数传递给std::is_convertible_v这类模板元工具,这种非法写法导致编译器内部逻辑崩溃。
正确判断类型是否为Range的方式
C++20中判断一个类型是否符合std::ranges::range概念,有两种标准方式:
- 使用
requires表达式结合if constexpr:直接在编译期分支中用概念检查类型if constexpr (std::ranges::range<decltype(elem)>) { // elem是Range类型 } - 自定义类型 trait:将概念转换为编译期常量值,方便复用
template<typename T> struct is_range : std::false_type {}; template<std::ranges::range T> struct is_range<T> : std::true_type {}; template<typename T> constexpr bool is_range_v = is_range<T>::value;
替代实现方案
方案1:利用函数重载(更符合C++20概念设计)
通过重载区分Range类型和非Range类型,自动递归处理嵌套结构,同时修复原代码末尾多余逗号的问题:
#include <iostream> #include <ranges> #include <vector> // 处理非Range类型:直接输出值 template<typename T> requires (!std::ranges::range<T>) void print(const T& value) { std::cout << value; } // 处理Range类型:递归打印每个元素 std::ranges::range auto&& print(std::ranges::range auto&& range) { std::cout << "{"; bool first = true; for (auto&& elem : range) { if (!first) { std::cout << ", "; } first = false; print(elem); // 根据elem类型自动匹配对应重载 } std::cout << "}"; return std::forward<decltype(range)>(range); } // 测试示例 int main() { std::vector<std::vector<int>> nested_data = {{1, 2, 3}, {4, 5}, {6}}; print(nested_data); std::cout << "\n"; std::vector<int> flat_data = {10, 20, 30}; print(flat_data); std::cout << "\n"; return 0; }
方案2:单函数内用if constexpr分支判断
在同一个print函数中,通过编译期分支判断元素是否为Range,实现递归:
#include <iostream> #include <ranges> #include <vector> void print(std::ranges::range auto&& range) { std::cout << "{"; bool first = true; for (auto&& elem : range) { if (!first) { std::cout << ", "; } first = false; // 编译期判断elem是否为Range,决定递归还是直接输出 if constexpr (std::ranges::range<decltype(elem)>) { print(elem); } else { std::cout << elem; } } std::cout << "}"; } // 测试示例 int main() { std::vector<std::vector<int>> nested_data = {{1, 2, 3}, {4, 5}, {6}}; print(nested_data); std::cout << "\n"; return 0; }
内容的提问来源于stack exchange,提问作者Arnab Mahanti
相关产品推荐
相关产品推荐

