Объединение адаптеров диапазонов и 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
, пять раз.