casting a returned iterator to const
casting a returned iterator to const
I have the following for
statement in my code:
for
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.
const_iterator
cbegin()
cbegin()
cend()
stable_partition
stable_partition
non const_iterators
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.
std::vector< std::string >::const_iterator
iterator
const_iterator
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?
auto
I would definitely move all this stuff out of loop prologue.
auto IterTarget.cbegin(); decltype(Target)::const_iterator const IterEndstd::stable_partition(Target.begin(), Target.end(), Check);
– VTT
Aug 29 at 10:56
auto IterTarget.cbegin(); decltype(Target)::const_iterator const IterEndstd::stable_partition(Target.begin(), Target.end(), Check);
you can use
decltype(Target.cbegin()) IterEnd
– Nishant Singh
Aug 29 at 10:57
decltype(Target.cbegin()) IterEnd
Careful with your terminology: making the iterator const doesn't make sense as you'll be incrementing it. ;)
– Lightness Races in Orbit
Aug 29 at 11:01
Also note that
stable_partition
reorders the elements of Target
(and possibly the first), so Iter
might not point to the first element (depends on the container used). Invoke stable_partition
first before you initialize Iter
.– dan
Aug 29 at 11:09
stable_partition
Target
Iter
stable_partition
Iter
2 Answers
2
The most clear solution in my opinion is to pull std::stable_partition
before the for
. This will result in an equivalent algorithm.
std::stable_partition
for
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:
stable_partition
container::iterator
container::const_iterator
IterEnd
std::vector<T::const_iterator
decltyp(Target.cbegin()
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
for (auto Iter = Target.cbegin(),
IterEnd = (decltype(Iter)) std::stable_partition(Target.begin(), Target.end(), Check);
Iter != IterEnd;
++Iter)
The call to
std::stable_partition
is in the preamble, so while philosophically we may claim it's "during the loop", it's not really, and thus your solution is not only good but also equivalent to the request. +1 but consider de-wrongising that opening para– Lightness Races in Orbit
Aug 29 at 11:02
std::stable_partition
@LightnessRacesinOrbit yes, I see your point. Edited.
– bolov
Aug 29 at 11:05
You still say that the OP "needs" to pull
std::stable_partition
before the for
, which is not true, though it does look much better this way (and I'm not sure the OP can get exactly what they want without it due to the single declaration statement in the preamble mixed with for
- perhaps that's what you meant acutally?)– Lightness Races in Orbit
Aug 29 at 11:09
std::stable_partition
for
for
@LightnessRacesinOrbit hm... he could by using a cast
– bolov
Aug 29 at 11:10
Indeed but I believe the point of the question is "cast to what? (without writing everything out rigidly)" - your answer does solve that perfectly by splitting the declaration into two, allowing us to use auto/decltype at both stages.
– Lightness Races in Orbit
Aug 29 at 11:12
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
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I am not sure about your idea of type unmanageability. Being explicit in your type declaration, if that solves your problem, is a fine approach.
auto
is convenient, but I wouldn’t say it is necessarily more manageable than being clear on the types you are using.– Brian
Aug 29 at 10:53