cppfront
cppfront copied to clipboard
[BUG] Function expression (lambda) cannot capture (and call) a reference to a callable passed via forwarding reference
Describe the bug A Cpp2 function expression cannot capture a reference to a callable passed via a forwarding reference (and then call it), meaning that the callable has to be copy constructed instead.
To Reproduce Run cppfront on this code:
is_even_functor: type = {
operator=: (out this) = { std::cout << "is_even_functor: ctor\n"; }
operator=: (out this, that) = { std::cout << "is_even_functor: copy ctor\n"; }
operator(): (this, x: int) -> bool = {
return x % 2 == 0;
}
}
ranges: namespace == std::ranges;
find_match_cpp2: (forward r, forward pred) -> bool = {
it:= ranges::find_if(r, :(x) pred$(x)); // Copies `pred`
// ranges::find_if(r, :(x) pred&$(x)); // Error
// ranges::find_if(r, :(x) pred&$*(x)); // Error
return it != ranges::cend(r);
}
bool find_match_cpp1(auto &&r, auto &&pred) {
// No copy of `pred`
auto it = ranges::find_if(r, [&pred](auto x) -> bool { return pred(x); });
return it != ranges::cend(r);
}
main: () -> int = {
v: std::array = (1, 2, 3, 4);
is_even: is_even_functor = ();
std::cout << "\nCall Cpp2\n";
std::println("Found an even number: {}", find_match_cpp2(v, is_even));
std::cout << "\nCall Cpp1\n";
std::println("Found an even number: {}", find_match_cpp1(v, is_even));
return 0;
}
Repro on Godbolt
The program output shows the undesired copy of is_even
:
is_even_functor: ctor
Call Cpp2
is_even_functor: copy ctor <--- Don't want this
Found an even number: true
Call Cpp1
Found an even number: true
The find_match_cpp1
C++ function demonstrates the desired behaviour. No copy of pred
should occur.
But in find_match_cpp2
the capture of pred
causes a copy:
[_0 = CPP2_FORWARD(pred)]
This Cpp2 code to capture pred
by reference:
it:= ranges::find_if(r, :(x) pred&$(x));
results in this C++ capture:
[_0 = (&CPP2_FORWARD(pred))]
but the call to _0
fails since it's a pointer that needs to be dereferenced.
This Cpp2 code to capture pred
by reference and then dereference it:
it:= ranges::find_if(r, :(x) pred&$*(x));
results in the same C++ capture as above (_0 = (&CPP2_FORWARD(pred)
) but lowers to the following:
return _0 * (x);
which results in a C++ compiler error.
This Cpp2 code succeeds:
it:= ranges::find_if(r, :(x) std::invoke(pred&$*, x));
but is less friendly to write (IMO) than the original C++.
Additional context
I was translating the ranges::adjacent_find
code from cppreference:
constexpr bool some_of(auto&& r, auto&& pred) // some but not all
{
return std::ranges::cend(r) != std::ranges::adjacent_find(r,
[&pred](auto const& x, auto const& y)
{
return pred(x) != pred(y);
});
}