Reputation: 932
I have the following for
statement in my code:
for (auto Iter = Target.begin(),
IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);
Iter != IterEnd; ++Iter)
{ /* loop statement */ }
The point is that the loop doesn't modify elements of the container, so it would make sense to declare iterators as const_iterator
. I can easily solve the problem for the first using cbegin()
, but the second is more complex. I can't declare cbegin()
and cend()
inside stable_partition
, since of course stable_partition
needs non const_iterators
to do its work.
A possible solution is to replace auto with the proper type, which in this case was std::vector< std::string >::const_iterator
. This forces the conversion from iterator
to const_iterator
on the second assignment.
Though, I don't like it. Types can easily and rapidly become unmanageable, so I'm looking for a solution that lets me use auto, without the need to declare something weird outside the loop. Any suggestion?
Upvotes: 4
Views: 718
Reputation: 75698
The most clear solution in my opinion is to pull std::stable_partition
before the for
. This will result in an equivalent algorithm.
The problem is that stable_partition
returns a iterator that can modify elements. Fortunately there is an implicit conversion from container::iterator
to container::const_iterator
(for most standard containers). To make the conversion you can specify the type of IterEnd
with std::vector<T::const_iterator
, or decltyp(Target.cbegin()
or my personal preference:
auto Iter = Target.cbegin();
decltype(Iter) IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);
for (; Iter != IterEnd; ++Iter)
{
}
For completeness you could keep all inside the for
if you wish but it's less readable in my opinion:
for (auto Iter = Target.cbegin(),
IterEnd = (decltype(Iter)) std::stable_partition(Target.begin(), Target.end(), Check);
Iter != IterEnd;
++Iter)
{}
Upvotes: 2
Reputation: 69864
Here's one way to express the idea through a functional interface:
#include <vector>
#include <algorithm>
#include <iostream>
namespace detail {
template<class Container, class F>
struct const_partitioned_target
{
using container_type = std::decay_t<Container>;
using const_iterator = typename container_type::const_iterator;
const_partitioned_target(Container& cont, F f)
: first(cont.cbegin())
, last(std::partition(cont.begin(), cont.end(), f))
{
}
const_iterator begin() const { return first; }
const_iterator end() const { return last; }
const_iterator first, last;
};
}
template<class Container, class F>
auto const_partitioned_target(Container& cont, F&& f)
{
return detail::const_partitioned_target<Container, std::decay_t<F>>(cont, std::forward<F>(f));
};
int main()
{
std::vector<int> Target { 1, 2, 6, 9, 10, 20, 30, 40 };
auto Check = [](auto&& x)
{
return x < 10;
};
for(auto&& elem : const_partitioned_target(Target, Check))
{
// elem will have the type: int const&
std::cout << elem << '\n';
}
}
expected output:
1
2
6
9
Upvotes: 0