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

Combining ranges adaptors and std::source_location got strange results

考虑 following useless code:

#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都有bug?

gcc 正确,程序完全有效。

And GCC outputs strange line number 61 no matter which row the std::source_location::current() is in:

这是因为默认函数参数 current() 是在函数 call 处求值的,这与函数所在位置无关 已声明.

并且这个函数被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 给您的行号,还打印了文件 name:[=27,这会更容易发现=]

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 的范围五次。