Consider the 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 rejects with the strange error message:
(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
]
And GCC outputs strange line number 61
no matter which row the std::source_location::current()
is in:
61
61
61
61
61
Is the above code well-formed? If so, does it mean that both MSVC and GCC have bugs?
gcc is correct, the program is completely valid.
And GCC outputs strange line number
61
no matter which row thestd::source_location::current()
is in:
That's because the default function argument, current()
, is evaluated at the point of the function call, which has nothing to do with where the function is declared.
And this function is called by transform_view
's iterator
's operator*()
. But not directly by operator*()
, that operator is going to call invoke
which itself is going to have to do a bunch of work to make sure it's invoked correctly. And the actual final overload in libstdc++'s implementation of invoke
that gets called is... oh, look at that, it's 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)...); }
This would've been easier to discover if instead of just printing the line number that source_location
gives you, you also printed the file name:
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);
Which prints a range containing the string /opt/compiler-explorer/gcc-trunk-20210817/include/c++/12.0.0/bits/invoke.h:61
, five times.