考虑以下无用的代码:
#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_view
的iterator
operator*()
调用的。但不是直接通过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
,五次。