为何仅在Visual Studio中ranges::count_if返回值类型依赖输入范围?
问题:std::ranges::count_if返回值导致Visual Studio编译失败
以下代码可在GCC和Clang中正常编译运行,但在Visual Studio中报错:
#include <functional> #include <algorithm> #include <ranges> #include <print> namespace r = std::ranges; namespace rv = std::ranges::views; int main() { auto test = r::count_if( rv::cartesian_product( rv::iota(0, 20), rv::iota(0, 20), rv::iota(0, 20) ), [&](auto&&) -> bool { return true; } ); std::println("{}", test); return 0; }
Visual Studio的核心报错信息为:
D:\work\stackoverflow\stackoverflow.cpp(19): error C3615: consteval function 'std::__p2286::_Compile_time_parse_format_specs' cannot result in a constant expression C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\format(3557): note: failure was caused by control reaching the end of a consteval function D:\work\stackoverflow\stackoverflow.cpp(19): note: the call stack of the evaluation (the oldest call first) is D:\work\stackoverflow\stackoverflow.cpp(19): note: while evaluating function 'std::basic_format_string<char,std::_Signed128 &>::basic_format_string<char[3]>(const _Ty (&))' ...
报错本质是count_if返回了128位整数类型,但std::println无法处理该类型。已知两种修复方式:
- 将
count_if的返回值强制转换为int - 移除笛卡尔积中的第三个
rv::iota(0, 20),仅保留两个范围
疑问:此情况是Visual Studio的实现错误,还是C++标准允许的行为?
分析与结论
1. 关于std::ranges::count_if的返回类型
C++标准规定,std::ranges::count_if的返回类型是iter_difference_t<iterator_t<Range>>,即对应范围迭代器的差值类型。对于rv::cartesian_product生成的范围,其迭代器差值类型的选择逻辑是能容纳所有输入范围大小的乘积。
虽然三个rv::iota(0,20)的乘积是8000,远小于32位int的最大值(2147483647),但标准并未强制要求必须使用最小的合适类型——实现可以选择更大的整数类型(比如128位)来提前规避溢出风险,这属于标准允许的行为。
2. 编译失败的核心原因
编译报错的直接问题在于Visual Studio的标准库未实现对128位整数的格式化支持。GCC和Clang的标准库已经支持用std::println/std::format处理128位整数,但Visual Studio的std::format组件目前还不支持该类型,导致编译时无法解析对应的格式说明符。
总结
count_if返回128位整数是标准允许的实现选择;- 编译失败是Visual Studio的标准库缺陷,因为它未支持128位整数的格式化,而这一功能在其他主流编译器中已实现。
内容的提问来源于stack exchange,提问作者jwezorek
相关产品推荐
相关产品推荐

