Объединение адаптеров диапазонов и 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 имеют ошибки?

1 ответ

Решение

gcc верен, программа полностью верна.

И GCC выводит странный номер строки 61 независимо от того, в какой строке std::source_location::current() в:

Это потому, что аргумент функции по умолчанию, current(), оценивается в момент вызова функции , который не имеет ничего общего с тем, где функция объявлена.

И эта функция вызывается transform_viewс iteratorс. Но не напрямую operator*(), этот оператор будет вызывать, который сам должен будет проделать кучу работы, чтобы убедиться, что он вызывается правильно. И фактическая окончательная перегрузка в реализации 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, пять раз.

Другие вопросы по тегам