I have this function which uses std::partition to split a container into two based on a predicate.
template<class Container, class Predicate>
void split_partition(Container& src, Container& dest, Predicate p)
{
auto ends = std::partition_copy(
std::make_move_iterator( src.begin() ), std::make_move_iterator( src.end() ),
src.begin(), std::back_inserter(dest), p
);
src.erase(ends.first, src.end());
}
I tried calling it with
split_partition(a, b, [](auto& t) {return t.i > 4; });
but it didn't even compile, now if use const it does work.
split_partition(a, b, [](const auto& t) {return t.i > 4; });
Why is that?
Edit:
std::vector<Thing> a = { {0, "foo"}, {1, "bar"}, {5, "what"}, {8, "yo"}, {2, ""} };
std::vector<Thing> b;
You are calling
std::partition
with a moved-from range:This means that the lambda you have is going to be invoked with some
Object&&
orconst Object&&
.const auto&
can bind to it in either case.const auto&
is universal in the sense that it can bind to any value category (lvalue, xvalue, prvalue).For example:
This code compiles to
This means that no constructor of
S
was called,const&
simply binds to anything. Since you're usingstd::move_iterator
(which is pointless for the purpose of comparison), your case is eitherr1
orr3
.auto&
isn't quite so powerful: a non-const lvalue reference cannot bind to a const xvalue. This would ber3
, and that's the case you're running into.- [dcl.init.ref] p5.2
Solution
Just accept
const auto&
in your lambda. It's more correct since you're just comparing elements without modifying them anyway.