Leo*_*Leo 5 c++ implicit-conversion std-ranges c++23
我偶然发现了与range一起operator<<使用时存在不明确重载的问题。\n更具体地说,使用以下代码:std::views::enumeratesize_t
#include <iostream>\n#include <ranges>\nnamespace rv = std::ranges::views;\n\nint main()\n{\n for (const auto& [idx, value] : rv::iota(0zu, 5zu) | rv::enumerate)\n std::cout << idx << '\\n';\n}\nRun Code Online (Sandbox Code Playgroud)\n并gcc 13.1.1在 Linux 上使用以下命令进行编译:g++ --std=c++23 main.cpp.\n我收到错误:
main.cpp: In function \xe2\x80\x98int main()\xe2\x80\x99:\nmain.cpp:11:19: error: ambiguous overload for \xe2\x80\x98operator<<\xe2\x80\x99 (operand types are \xe2\x80\x98std::ostream\xe2\x80\x99 {aka \xe2\x80\x98std::basic_ostream<char>\xe2\x80\x99} and \xe2\x80\x98std::tuple_element<0, const std::tuple<__int128, long unsigned int> >::type\xe2\x80\x99 {aka \xe2\x80\x98const __int128\xe2\x80\x99})\n 11 | std::cout << idx << '\\n';\n | ~~~~~~~~~ ^~ ~\n | | |\n | | std::tuple_element<0, const std::tuple<__int128, long unsigned int> >::type {aka const __int128}\n | std::ostream {aka std::basic_ostream<char>}\nRun Code Online (Sandbox Code Playgroud)\n然后是一堆<<运营商候选人。
idx这可以通过强制转换(例如,使用)来修复std::cout << (size_t)idx,但似乎不必要的繁琐。\n这个问题似乎只在使用long变量作为范围的开始和结束时出现。\n例如,如果我们使用rv::iota(begin, end)with 之一int begin{0}, end{5}(无论是有符号还是无符号)问题消失了。
这是编译器级别的一个简单错误还是有更深层次的原因阻止它匹配正确的类型?
\n的类型idx是视图difference_type的类型iota。
您正在使用 的std::size_t参数类型iota。您的系统上可能std::size_t有 64 位宽度,这也可能是您系统上任何整数类型的最大宽度。
现在的问题是difference_type视图iota不能也是 64 位宽度类型,因为这样的类型无法保存范围内任何两个项目之间的差异。
因此iota指定 adifference_type的宽度大于元素类型的宽度。如果存在具有该属性的有符号整数类型,则将difference_type是有符号整数类型。如果这样的整数不存在,那么difference_type将是一个具有足够宽度的类有符号整数类型,即一种在某些重要方面表现得像有符号整数类型的类型,但不是一种类型。
例如,它可能是具有正确重载运算符的类类型,或者如您所见,某些特定于实现的类型不被视为整数类型,但具有类似的行为。(实际上,GCC是否考虑__int128扩展整数类型取决于您是否使用-std=gnu++23或-std=c++23。)
有关这些类型需要满足的属性列表,请参阅[iterator.concept.winc]。
但是,std::ostream::operator<<仅对标准整数类型进行重载。idx因此,如果它只是类似有符号整数的类型,则它的类型不具有完全匹配的重载。
您会收到歧义错误,因为实现选择的具体类型idxie__in128_t可以隐式转换为具有相同转换等级的所有标准有符号整数类型。__int128_t(如果被视为(扩展)有符号整数类型,情况也会如此。)但是,这是一个实现细节。甚至不能保证类似有符号整数的类型可以隐式转换为任何整数类型(因为它们的宽度都较小)。
使用例如static_cast或 C 风格强制转换的显式转换保证适用于任何整数类型,但存在缩小结果范围的风险。
所以这里不存在编译器错误。这是允许的行为,您不能依赖直接idx打印operator<<。
如果您只想考虑toiota的范围,最简单的解决方案是不使用 type ,而是使用足够大小的较小类型,例如,如果您知道它是 32 位并且您使用的是 64 位系统。05std::size_trv::iota(0, 5)int
如果您不想冒险缩小范围,并且不想依赖于知道系统上存在具有更大宽度的标准整数类型,那么您必须自己将 的值转换idx为十进制字符串表示形式。保证通常的算术运算符能够按预期工作在类似有符号整数的类型上,因此这是可能的。但我认为目前没有任何标准库函数可以将任何有符号整数类型转换为十进制字符串表示形式。