将范围适配器和 std::source_location 结合起来得到了奇怪的结果



考虑以下无用的代码:

#include <ranges>
#include <source_location>
#include <iostream>
int main() {
auto lines = std::views::iota(0, 5)
| std::views::transform(
[](int, const std::source_location& location = std::source_location::current()) 
{ return location.line(); }
);
for (const auto& line : lines)
std::cout << line << "n";
}

MSVC 拒绝并显示奇怪的错误消息:

(7): error C2676: binary '|': 'std::ranges::iota_view<_Ty1,_Ty2>' does not define this operator or a conversion to a type acceptable to the predefined operator
with
[
_Ty1=int,
_Ty2=int
]

GCC 输出奇怪的行号61无论std::source_location::current()在哪一行:

61
61
61
61
61

上面的代码格式是否正确?如果是这样,这是否意味着MSVC和GCC都有错误?

gcc 是正确的,该程序是完全有效的。

GCC 输出奇怪的行号61无论std::source_location::current()在哪一行:

这是因为默认函数参数current()是在函数调用时计算的,这与声明函数的位置无关。

而这个函数是由transform_viewiteratoroperator*()调用的。但不是直接通过operator*(),该运算符将调用invoke它本身将不得不做一堆工作以确保它被正确调用。libstdc++ 实现中被调用的invoke的实际最终重载是......哦,看看那个,它bits/invoke.h:61

template<typename _Res, typename _Fn, typename... _Args>
constexpr _Res
__invoke_impl(__invoke_other, _Fn&& __f, _Args&&... __args)
{ return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }

如果不仅打印source_location给你的行号,还打印文件名,这将更容易发现:

auto lines = std::views::iota(0, 5)
| std::views::transform(
[](int, const std::source_location& location = std::source_location::current()) 
{ return fmt::format("{}:{}", location.file_name(), location.line()); }
);
fmt::print("{}n", lines);

它打印一个包含字符串的范围/opt/compiler-explorer/gcc-trunk-20210817/include/c++/12.0.0/bits/invoke.h:61,五次。

相关内容

最新更新