2

考虑以下无用的代码

#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
            ]

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

61
61
61
61
61

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

4

1 回答 1

3

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

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

那是因为默认函数参数 ,current()是在函数调用点进行评估的,这与函数的声明位置无关。

而这个函数是由transform_view's调用iteratoroperator*()。但不是直接 by operator*(),该运算符将调用invokewhich 本身将必须做大量工作以确保正确调用它。而 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);

它将包含 string 的范围打印/opt/compiler-explorer/gcc-trunk-20210817/include/c++/12.0.0/bits/invoke.h:61五次。

于 2021-08-17T16:57:00.450 回答