c++ conditional uni-directional iteratorNo known conversionsWhat are the differences between a pointer variable and a reference variable in C++?How do I iterate over the words of a string?How can I profile C++ code running on Linux?The Definitive C++ Book Guide and ListWhat is the effect of extern “C” in C++?What is the “-->” operator in C++?Why do we need virtual functions in C++?Easiest way to convert int to string in C++C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?Why is reading lines from stdin much slower in C++ than Python?
Under what law can the U.S. arrest International Criminal Court (ICC) judges over war crimes probe?
How bitcoin nodes update UTXO set when their latests blocks are replaced?
Is it ok to put a subplot to a story that is never meant to contribute to the development of the main plot?
I think I may have violated academic integrity last year - what should I do?
How to prevent bad sectors?
Would the Geas spell work in a dead magic zone once you enter it?
Were pen cap holes designed to prevent death by suffocation if swallowed?
Can a wire having a 610-670 THz (frequency of blue light) AC frequency supply, generate blue light?
Tabulated absorption spectra of greenhouse gases?
I unknowingly submitted plagiarised work
Command to Search for Filenames Exceeding 143 Characters?
Is the first derivative operation on a signal a causal system?
Why doesn't the Earth's acceleration towards the Moon accumulate to push the Earth off its orbit?
Integrating an absolute function using Mathematica
Is one obligated to listen to a Rav?
Is there a general effective method to solve Smullyan style Knights and Knaves problems? Is the truth table method the most appropriate one?
How were these pictures of spacecraft wind tunnel testing taken?
Is there a down side to setting the sampling time of a SAR ADC as long as possible?
Mother abusing my finances
Do you play the upbeat when beginning to play a series of notes, and then after?
Graph with same number of edges and vertices is a loop?
What is the object moving across the ceiling in this stock footage?
Why does the 'metric Lagrangian' approach appear to fail in Newtonian mechanics?
Is this story about US tax office reasonable?
c++ conditional uni-directional iterator
No known conversionsWhat are the differences between a pointer variable and a reference variable in C++?How do I iterate over the words of a string?How can I profile C++ code running on Linux?The Definitive C++ Book Guide and ListWhat is the effect of extern “C” in C++?What is the “-->” operator in C++?Why do we need virtual functions in C++?Easiest way to convert int to string in C++C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?Why is reading lines from stdin much slower in C++ than Python?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I want to achieve something like the pseudo-code below:
string foo; // or vector<int> foo;
auto itr = bar? foo.begin() : foo.rbegin();
auto end = bar? foo.end() : foo.rend();
for ( ; itr != end; ++itr)
// SomeAction ...
That is, I want to set itr
to be either a forward iterator or the reverse iterator, depending on some condition bar
, to scan in forward or reverse direction.
Apparently code like that won't work, since forward iterator and reverse iterator has different type.
Note that I don't want to split into two loops, as those code like // SomeAction
will be duplicated.
How can I do that?
Answers using C++11 and/or before are preferred.
Also, please elaborate if string and vector have different solutions.
c++ c++11
add a comment |
I want to achieve something like the pseudo-code below:
string foo; // or vector<int> foo;
auto itr = bar? foo.begin() : foo.rbegin();
auto end = bar? foo.end() : foo.rend();
for ( ; itr != end; ++itr)
// SomeAction ...
That is, I want to set itr
to be either a forward iterator or the reverse iterator, depending on some condition bar
, to scan in forward or reverse direction.
Apparently code like that won't work, since forward iterator and reverse iterator has different type.
Note that I don't want to split into two loops, as those code like // SomeAction
will be duplicated.
How can I do that?
Answers using C++11 and/or before are preferred.
Also, please elaborate if string and vector have different solutions.
c++ c++11
1
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at leastoperator++()
andoperator!=()
.
– Scheff
May 14 at 15:09
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Isbar
a compile-time constant?
– YSC
May 14 at 15:12
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23
add a comment |
I want to achieve something like the pseudo-code below:
string foo; // or vector<int> foo;
auto itr = bar? foo.begin() : foo.rbegin();
auto end = bar? foo.end() : foo.rend();
for ( ; itr != end; ++itr)
// SomeAction ...
That is, I want to set itr
to be either a forward iterator or the reverse iterator, depending on some condition bar
, to scan in forward or reverse direction.
Apparently code like that won't work, since forward iterator and reverse iterator has different type.
Note that I don't want to split into two loops, as those code like // SomeAction
will be duplicated.
How can I do that?
Answers using C++11 and/or before are preferred.
Also, please elaborate if string and vector have different solutions.
c++ c++11
I want to achieve something like the pseudo-code below:
string foo; // or vector<int> foo;
auto itr = bar? foo.begin() : foo.rbegin();
auto end = bar? foo.end() : foo.rend();
for ( ; itr != end; ++itr)
// SomeAction ...
That is, I want to set itr
to be either a forward iterator or the reverse iterator, depending on some condition bar
, to scan in forward or reverse direction.
Apparently code like that won't work, since forward iterator and reverse iterator has different type.
Note that I don't want to split into two loops, as those code like // SomeAction
will be duplicated.
How can I do that?
Answers using C++11 and/or before are preferred.
Also, please elaborate if string and vector have different solutions.
c++ c++11
c++ c++11
asked May 14 at 15:07
Robin HsuRobin Hsu
2,1141921
2,1141921
1
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at leastoperator++()
andoperator!=()
.
– Scheff
May 14 at 15:09
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Isbar
a compile-time constant?
– YSC
May 14 at 15:12
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23
add a comment |
1
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at leastoperator++()
andoperator!=()
.
– Scheff
May 14 at 15:09
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Isbar
a compile-time constant?
– YSC
May 14 at 15:12
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23
1
1
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at least
operator++()
and operator!=()
.– Scheff
May 14 at 15:09
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at least
operator++()
and operator!=()
.– Scheff
May 14 at 15:09
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Is
bar
a compile-time constant?– YSC
May 14 at 15:12
Is
bar
a compile-time constant?– YSC
May 14 at 15:12
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23
add a comment |
5 Answers
5
active
oldest
votes
I would put the logic in a two-iterator function:
<template typename Iter>
void do_stuff(Iter first, Iter last)
for(; first != last; ++first)
// Do logic
bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
7
This function should probably bestd::for_each()
.
– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
add a comment |
The forward and reverse iterators are different types for most if not all containers, so unfortunately if it is a runtime decision, they can not simply be assigned to the same variable through the use of auto.
One option is to move the use of them into a template function:
template<class Iterator> void loop(Iterator begin, Iterator end)
for (auto itr = begin; itr != end; ++itr) ...
if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
In newer versions of C++ (C++14 and newer, so not C++11) the loop function can be a lambda, by using auto
as the parameter type.
auto loop = [](auto begin, auto end)
for (auto itr = begin; itr != end; ++itr) ...
;
Another option, although somewhat more involved would be to make a wrapper type that can contain either an iterator or reverse iterator, and acts like an iterator itself with at least the comparison, increment and dereference operators.
add a comment |
I don't want to split into two loops, as those code like // SomeAction will be duplicated.
Put the action into a lambda.
auto lambda = [&](char &val) // `ElementType &`
// ...
;
if (bar)
for (auto &val : foo)
lambda(val);
else
for (auto it = foo.rbegin(); it != foo.rend(); it++)
lambda(*it);
Alternatively, use indices rather than iterators. This will only work with containers that allow random access.
std::size_t i, end, step;
if (bar)
i = 0;
end = foo.size();
step = 1;
else
i = foo.size() - 1;
end = -1;
step = -1;
for (; i != end; i += step)
// ...
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
add a comment |
One option is to to write a function template for the loop that works with any iterator. Then conditionally call one instance of the template or another. The other answers already show examples of how to do that.
By the way, the loop template might already exist in <algorithm>
header depending on what you're doing. You could possibly use (but are not limited to) either std::for_each
, std::accumulate
or std::remove
for example:
auto body = [captures,needed,by,some,action](char c)
// SomeAction ...
;
if (bar)
std::for_each(foo.begin(), foo.end(), body);
else
std::for_each(foo.rbegin(), foo.rend(), body);
If the body of the loop is reusable beyond this context, then you could use a named function as well, but only if you don't need captures. With captures, you can use a named functor type, but that involves quite a bit boilerplate.
Another option is to use a type erasing iterator adaptor. It comes with a small runtime cost, and is probably not necessary here. But it is useful to mention in case people have a closely related problem where it is a better fit.
Essentially, such adaptor is to templated iterator what std::function
is to a templated functor argument. It removes the need for templates, which can be useful for abstract interfaces in particular. Unfortunately though, the standard library doesn't provide such iterator adaptor.
An alterantive for an iterator adaptor is a range adaptor (also not in standard library):
using t_erase = boost::adaptors::type_erased<>;
auto range = bar
? boost::make_iterator_range(foo.begin(), foo.end()) | t_erase()
: boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range)
// SomeAction ...
add a comment |
Use an iterator class that abstracts this funtionality.
An iterator has 3 basic functions:
- Increment
- Dereference
- Check for equality
We can use these as guidelines in creating an interface which abstracts this behavior. This interface is a bit cumbersome to use on it's own, but we can use it to build a wrapper class, GenericIterator
, that can automatically be assigned any other iterator type.
GenericIterator
: A ready-made general purpose solution
It's possible to write a GenericIterator
class that can be assigned iterators from pretty much any collection, including the reverse iterators.
int main()
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values 1, 2, 3 ;
GenericIterator<int&> begin, end;
if(iterate_forward)
begin = values.begin();
end = values.end();
else
begin = values.rbegin();
end = values.rend();
// Print out the values
for(; begin != end; ++begin)
std::cout << *begin << " ";
You can download the entirety of the code from this github repo, which I'll be updating and improving as needed.
Remarks on performance GenericIterator
In terms of functionality, GenericIterator
gives you everything you could possibly ask for. It's light-weight; and it's convenient; and it's easy to re-purpose if your code needs to read from a std::list
or something other than a vector.
However, due to the fundamental limitations of runtime polymorphism, it's significantly more difficult for the compiler to inline the virtual method calls. This means that GenericIterator
carries more runtime overhead than other solutions.
It's a good idea to favor static polymorphism and templates over runtime polymorphism when possible. If you're able to do so, use something like Mark B's solution, which will ultimately be more performant.
Appendix
Iterator interface definition. This class is used to implement GenericIterator
. A GenericIterator
contains a pointer to IteratorBase
, which is used to achieve the runtime polymorphism. Thanks to the clone()
method, GenericIterator
remains both copyable and movable as expected.
template <class Value>
class IteratorBase
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
;
Concrete class implementing IteratorBase
. This class implements the behavior defined in IteratorBase
. The iterator it contains is the actual iterator returned by the collection you're iterating over. In your case, it'd be either std::vector::iterator
or std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it)
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override return *it;
IteratorBase<Value>& operator++() override
++it;
return *this;
bool operator!=(IteratorBase<Value> const& other) const override
bool operator==(IteratorBase<Value> const& other) const override
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
IteratorBase<Value>* clone() const override
return new IteratorDerived(*this);
;
GenericIterator
implementation. This is the actual implementation of GenericIterator
, based on IteratorBase
and IteratorDerived
. Any iterator given to GenericIterator
is wrapped in the corresponding IteratorDerived
, which is then assigned to the IteratorBase
pointer.
template <class Value>
class GenericIterator
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
GenericIterator& operator=(GenericIterator const& it)
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const return *(*iterator);
GenericIterator& operator++()
++(*iterator);
return *this;
void operator++(int)
++(*iterator);
bool operator==(GenericIterator const& other) const
return *iterator == *other.iterator;
bool operator!=(GenericIterator const& other) const
return *iterator != *other.iterator;
;
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56133546%2fc-conditional-uni-directional-iterator%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
I would put the logic in a two-iterator function:
<template typename Iter>
void do_stuff(Iter first, Iter last)
for(; first != last; ++first)
// Do logic
bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
7
This function should probably bestd::for_each()
.
– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
add a comment |
I would put the logic in a two-iterator function:
<template typename Iter>
void do_stuff(Iter first, Iter last)
for(; first != last; ++first)
// Do logic
bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
7
This function should probably bestd::for_each()
.
– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
add a comment |
I would put the logic in a two-iterator function:
<template typename Iter>
void do_stuff(Iter first, Iter last)
for(; first != last; ++first)
// Do logic
bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
I would put the logic in a two-iterator function:
<template typename Iter>
void do_stuff(Iter first, Iter last)
for(; first != last; ++first)
// Do logic
bar ? do_stuff(foo.begin(), foo.end()) : do_stuff(foo.rbegin(), foo.rend());
answered May 14 at 15:14
Mark BMark B
86.7k688168
86.7k688168
7
This function should probably bestd::for_each()
.
– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
add a comment |
7
This function should probably bestd::for_each()
.
– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
7
7
This function should probably be
std::for_each()
.– moooeeeep
May 15 at 9:28
This function should probably be
std::for_each()
.– moooeeeep
May 15 at 9:28
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
In simple cases that would definitely be better; I was (for no particular reason) assuming the logic was potentially more complicated where writing out the loop might make it clearer.
– Mark B
May 15 at 17:00
add a comment |
The forward and reverse iterators are different types for most if not all containers, so unfortunately if it is a runtime decision, they can not simply be assigned to the same variable through the use of auto.
One option is to move the use of them into a template function:
template<class Iterator> void loop(Iterator begin, Iterator end)
for (auto itr = begin; itr != end; ++itr) ...
if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
In newer versions of C++ (C++14 and newer, so not C++11) the loop function can be a lambda, by using auto
as the parameter type.
auto loop = [](auto begin, auto end)
for (auto itr = begin; itr != end; ++itr) ...
;
Another option, although somewhat more involved would be to make a wrapper type that can contain either an iterator or reverse iterator, and acts like an iterator itself with at least the comparison, increment and dereference operators.
add a comment |
The forward and reverse iterators are different types for most if not all containers, so unfortunately if it is a runtime decision, they can not simply be assigned to the same variable through the use of auto.
One option is to move the use of them into a template function:
template<class Iterator> void loop(Iterator begin, Iterator end)
for (auto itr = begin; itr != end; ++itr) ...
if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
In newer versions of C++ (C++14 and newer, so not C++11) the loop function can be a lambda, by using auto
as the parameter type.
auto loop = [](auto begin, auto end)
for (auto itr = begin; itr != end; ++itr) ...
;
Another option, although somewhat more involved would be to make a wrapper type that can contain either an iterator or reverse iterator, and acts like an iterator itself with at least the comparison, increment and dereference operators.
add a comment |
The forward and reverse iterators are different types for most if not all containers, so unfortunately if it is a runtime decision, they can not simply be assigned to the same variable through the use of auto.
One option is to move the use of them into a template function:
template<class Iterator> void loop(Iterator begin, Iterator end)
for (auto itr = begin; itr != end; ++itr) ...
if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
In newer versions of C++ (C++14 and newer, so not C++11) the loop function can be a lambda, by using auto
as the parameter type.
auto loop = [](auto begin, auto end)
for (auto itr = begin; itr != end; ++itr) ...
;
Another option, although somewhat more involved would be to make a wrapper type that can contain either an iterator or reverse iterator, and acts like an iterator itself with at least the comparison, increment and dereference operators.
The forward and reverse iterators are different types for most if not all containers, so unfortunately if it is a runtime decision, they can not simply be assigned to the same variable through the use of auto.
One option is to move the use of them into a template function:
template<class Iterator> void loop(Iterator begin, Iterator end)
for (auto itr = begin; itr != end; ++itr) ...
if (bar) loop(foo.begin(), foo.end());
else loop(foo.rbegin(), foo.rend());
In newer versions of C++ (C++14 and newer, so not C++11) the loop function can be a lambda, by using auto
as the parameter type.
auto loop = [](auto begin, auto end)
for (auto itr = begin; itr != end; ++itr) ...
;
Another option, although somewhat more involved would be to make a wrapper type that can contain either an iterator or reverse iterator, and acts like an iterator itself with at least the comparison, increment and dereference operators.
edited May 14 at 15:22
answered May 14 at 15:14
Fire LancerFire Lancer
17k2592156
17k2592156
add a comment |
add a comment |
I don't want to split into two loops, as those code like // SomeAction will be duplicated.
Put the action into a lambda.
auto lambda = [&](char &val) // `ElementType &`
// ...
;
if (bar)
for (auto &val : foo)
lambda(val);
else
for (auto it = foo.rbegin(); it != foo.rend(); it++)
lambda(*it);
Alternatively, use indices rather than iterators. This will only work with containers that allow random access.
std::size_t i, end, step;
if (bar)
i = 0;
end = foo.size();
step = 1;
else
i = foo.size() - 1;
end = -1;
step = -1;
for (; i != end; i += step)
// ...
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
add a comment |
I don't want to split into two loops, as those code like // SomeAction will be duplicated.
Put the action into a lambda.
auto lambda = [&](char &val) // `ElementType &`
// ...
;
if (bar)
for (auto &val : foo)
lambda(val);
else
for (auto it = foo.rbegin(); it != foo.rend(); it++)
lambda(*it);
Alternatively, use indices rather than iterators. This will only work with containers that allow random access.
std::size_t i, end, step;
if (bar)
i = 0;
end = foo.size();
step = 1;
else
i = foo.size() - 1;
end = -1;
step = -1;
for (; i != end; i += step)
// ...
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
add a comment |
I don't want to split into two loops, as those code like // SomeAction will be duplicated.
Put the action into a lambda.
auto lambda = [&](char &val) // `ElementType &`
// ...
;
if (bar)
for (auto &val : foo)
lambda(val);
else
for (auto it = foo.rbegin(); it != foo.rend(); it++)
lambda(*it);
Alternatively, use indices rather than iterators. This will only work with containers that allow random access.
std::size_t i, end, step;
if (bar)
i = 0;
end = foo.size();
step = 1;
else
i = foo.size() - 1;
end = -1;
step = -1;
for (; i != end; i += step)
// ...
I don't want to split into two loops, as those code like // SomeAction will be duplicated.
Put the action into a lambda.
auto lambda = [&](char &val) // `ElementType &`
// ...
;
if (bar)
for (auto &val : foo)
lambda(val);
else
for (auto it = foo.rbegin(); it != foo.rend(); it++)
lambda(*it);
Alternatively, use indices rather than iterators. This will only work with containers that allow random access.
std::size_t i, end, step;
if (bar)
i = 0;
end = foo.size();
step = 1;
else
i = foo.size() - 1;
end = -1;
step = -1;
for (; i != end; i += step)
// ...
edited May 14 at 15:23
answered May 14 at 15:15
HolyBlackCatHolyBlackCat
18k33669
18k33669
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
add a comment |
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
Unfortunately, not a C++11 feature as the question was specifically tagged :(
– Fire Lancer
May 14 at 15:17
1
1
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
@FireLancer Forgot it was a C++14 thing, fixed.
– HolyBlackCat
May 14 at 15:23
add a comment |
One option is to to write a function template for the loop that works with any iterator. Then conditionally call one instance of the template or another. The other answers already show examples of how to do that.
By the way, the loop template might already exist in <algorithm>
header depending on what you're doing. You could possibly use (but are not limited to) either std::for_each
, std::accumulate
or std::remove
for example:
auto body = [captures,needed,by,some,action](char c)
// SomeAction ...
;
if (bar)
std::for_each(foo.begin(), foo.end(), body);
else
std::for_each(foo.rbegin(), foo.rend(), body);
If the body of the loop is reusable beyond this context, then you could use a named function as well, but only if you don't need captures. With captures, you can use a named functor type, but that involves quite a bit boilerplate.
Another option is to use a type erasing iterator adaptor. It comes with a small runtime cost, and is probably not necessary here. But it is useful to mention in case people have a closely related problem where it is a better fit.
Essentially, such adaptor is to templated iterator what std::function
is to a templated functor argument. It removes the need for templates, which can be useful for abstract interfaces in particular. Unfortunately though, the standard library doesn't provide such iterator adaptor.
An alterantive for an iterator adaptor is a range adaptor (also not in standard library):
using t_erase = boost::adaptors::type_erased<>;
auto range = bar
? boost::make_iterator_range(foo.begin(), foo.end()) | t_erase()
: boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range)
// SomeAction ...
add a comment |
One option is to to write a function template for the loop that works with any iterator. Then conditionally call one instance of the template or another. The other answers already show examples of how to do that.
By the way, the loop template might already exist in <algorithm>
header depending on what you're doing. You could possibly use (but are not limited to) either std::for_each
, std::accumulate
or std::remove
for example:
auto body = [captures,needed,by,some,action](char c)
// SomeAction ...
;
if (bar)
std::for_each(foo.begin(), foo.end(), body);
else
std::for_each(foo.rbegin(), foo.rend(), body);
If the body of the loop is reusable beyond this context, then you could use a named function as well, but only if you don't need captures. With captures, you can use a named functor type, but that involves quite a bit boilerplate.
Another option is to use a type erasing iterator adaptor. It comes with a small runtime cost, and is probably not necessary here. But it is useful to mention in case people have a closely related problem where it is a better fit.
Essentially, such adaptor is to templated iterator what std::function
is to a templated functor argument. It removes the need for templates, which can be useful for abstract interfaces in particular. Unfortunately though, the standard library doesn't provide such iterator adaptor.
An alterantive for an iterator adaptor is a range adaptor (also not in standard library):
using t_erase = boost::adaptors::type_erased<>;
auto range = bar
? boost::make_iterator_range(foo.begin(), foo.end()) | t_erase()
: boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range)
// SomeAction ...
add a comment |
One option is to to write a function template for the loop that works with any iterator. Then conditionally call one instance of the template or another. The other answers already show examples of how to do that.
By the way, the loop template might already exist in <algorithm>
header depending on what you're doing. You could possibly use (but are not limited to) either std::for_each
, std::accumulate
or std::remove
for example:
auto body = [captures,needed,by,some,action](char c)
// SomeAction ...
;
if (bar)
std::for_each(foo.begin(), foo.end(), body);
else
std::for_each(foo.rbegin(), foo.rend(), body);
If the body of the loop is reusable beyond this context, then you could use a named function as well, but only if you don't need captures. With captures, you can use a named functor type, but that involves quite a bit boilerplate.
Another option is to use a type erasing iterator adaptor. It comes with a small runtime cost, and is probably not necessary here. But it is useful to mention in case people have a closely related problem where it is a better fit.
Essentially, such adaptor is to templated iterator what std::function
is to a templated functor argument. It removes the need for templates, which can be useful for abstract interfaces in particular. Unfortunately though, the standard library doesn't provide such iterator adaptor.
An alterantive for an iterator adaptor is a range adaptor (also not in standard library):
using t_erase = boost::adaptors::type_erased<>;
auto range = bar
? boost::make_iterator_range(foo.begin(), foo.end()) | t_erase()
: boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range)
// SomeAction ...
One option is to to write a function template for the loop that works with any iterator. Then conditionally call one instance of the template or another. The other answers already show examples of how to do that.
By the way, the loop template might already exist in <algorithm>
header depending on what you're doing. You could possibly use (but are not limited to) either std::for_each
, std::accumulate
or std::remove
for example:
auto body = [captures,needed,by,some,action](char c)
// SomeAction ...
;
if (bar)
std::for_each(foo.begin(), foo.end(), body);
else
std::for_each(foo.rbegin(), foo.rend(), body);
If the body of the loop is reusable beyond this context, then you could use a named function as well, but only if you don't need captures. With captures, you can use a named functor type, but that involves quite a bit boilerplate.
Another option is to use a type erasing iterator adaptor. It comes with a small runtime cost, and is probably not necessary here. But it is useful to mention in case people have a closely related problem where it is a better fit.
Essentially, such adaptor is to templated iterator what std::function
is to a templated functor argument. It removes the need for templates, which can be useful for abstract interfaces in particular. Unfortunately though, the standard library doesn't provide such iterator adaptor.
An alterantive for an iterator adaptor is a range adaptor (also not in standard library):
using t_erase = boost::adaptors::type_erased<>;
auto range = bar
? boost::make_iterator_range(foo.begin(), foo.end()) | t_erase()
: boost::make_iterator_range(foo.rbegin(), foo.rend()) | t_erase();
for(char c : range)
// SomeAction ...
edited May 15 at 10:14
answered May 14 at 15:33
eerorikaeerorika
93.1k670139
93.1k670139
add a comment |
add a comment |
Use an iterator class that abstracts this funtionality.
An iterator has 3 basic functions:
- Increment
- Dereference
- Check for equality
We can use these as guidelines in creating an interface which abstracts this behavior. This interface is a bit cumbersome to use on it's own, but we can use it to build a wrapper class, GenericIterator
, that can automatically be assigned any other iterator type.
GenericIterator
: A ready-made general purpose solution
It's possible to write a GenericIterator
class that can be assigned iterators from pretty much any collection, including the reverse iterators.
int main()
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values 1, 2, 3 ;
GenericIterator<int&> begin, end;
if(iterate_forward)
begin = values.begin();
end = values.end();
else
begin = values.rbegin();
end = values.rend();
// Print out the values
for(; begin != end; ++begin)
std::cout << *begin << " ";
You can download the entirety of the code from this github repo, which I'll be updating and improving as needed.
Remarks on performance GenericIterator
In terms of functionality, GenericIterator
gives you everything you could possibly ask for. It's light-weight; and it's convenient; and it's easy to re-purpose if your code needs to read from a std::list
or something other than a vector.
However, due to the fundamental limitations of runtime polymorphism, it's significantly more difficult for the compiler to inline the virtual method calls. This means that GenericIterator
carries more runtime overhead than other solutions.
It's a good idea to favor static polymorphism and templates over runtime polymorphism when possible. If you're able to do so, use something like Mark B's solution, which will ultimately be more performant.
Appendix
Iterator interface definition. This class is used to implement GenericIterator
. A GenericIterator
contains a pointer to IteratorBase
, which is used to achieve the runtime polymorphism. Thanks to the clone()
method, GenericIterator
remains both copyable and movable as expected.
template <class Value>
class IteratorBase
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
;
Concrete class implementing IteratorBase
. This class implements the behavior defined in IteratorBase
. The iterator it contains is the actual iterator returned by the collection you're iterating over. In your case, it'd be either std::vector::iterator
or std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it)
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override return *it;
IteratorBase<Value>& operator++() override
++it;
return *this;
bool operator!=(IteratorBase<Value> const& other) const override
bool operator==(IteratorBase<Value> const& other) const override
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
IteratorBase<Value>* clone() const override
return new IteratorDerived(*this);
;
GenericIterator
implementation. This is the actual implementation of GenericIterator
, based on IteratorBase
and IteratorDerived
. Any iterator given to GenericIterator
is wrapped in the corresponding IteratorDerived
, which is then assigned to the IteratorBase
pointer.
template <class Value>
class GenericIterator
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
GenericIterator& operator=(GenericIterator const& it)
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const return *(*iterator);
GenericIterator& operator++()
++(*iterator);
return *this;
void operator++(int)
++(*iterator);
bool operator==(GenericIterator const& other) const
return *iterator == *other.iterator;
bool operator!=(GenericIterator const& other) const
return *iterator != *other.iterator;
;
add a comment |
Use an iterator class that abstracts this funtionality.
An iterator has 3 basic functions:
- Increment
- Dereference
- Check for equality
We can use these as guidelines in creating an interface which abstracts this behavior. This interface is a bit cumbersome to use on it's own, but we can use it to build a wrapper class, GenericIterator
, that can automatically be assigned any other iterator type.
GenericIterator
: A ready-made general purpose solution
It's possible to write a GenericIterator
class that can be assigned iterators from pretty much any collection, including the reverse iterators.
int main()
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values 1, 2, 3 ;
GenericIterator<int&> begin, end;
if(iterate_forward)
begin = values.begin();
end = values.end();
else
begin = values.rbegin();
end = values.rend();
// Print out the values
for(; begin != end; ++begin)
std::cout << *begin << " ";
You can download the entirety of the code from this github repo, which I'll be updating and improving as needed.
Remarks on performance GenericIterator
In terms of functionality, GenericIterator
gives you everything you could possibly ask for. It's light-weight; and it's convenient; and it's easy to re-purpose if your code needs to read from a std::list
or something other than a vector.
However, due to the fundamental limitations of runtime polymorphism, it's significantly more difficult for the compiler to inline the virtual method calls. This means that GenericIterator
carries more runtime overhead than other solutions.
It's a good idea to favor static polymorphism and templates over runtime polymorphism when possible. If you're able to do so, use something like Mark B's solution, which will ultimately be more performant.
Appendix
Iterator interface definition. This class is used to implement GenericIterator
. A GenericIterator
contains a pointer to IteratorBase
, which is used to achieve the runtime polymorphism. Thanks to the clone()
method, GenericIterator
remains both copyable and movable as expected.
template <class Value>
class IteratorBase
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
;
Concrete class implementing IteratorBase
. This class implements the behavior defined in IteratorBase
. The iterator it contains is the actual iterator returned by the collection you're iterating over. In your case, it'd be either std::vector::iterator
or std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it)
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override return *it;
IteratorBase<Value>& operator++() override
++it;
return *this;
bool operator!=(IteratorBase<Value> const& other) const override
bool operator==(IteratorBase<Value> const& other) const override
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
IteratorBase<Value>* clone() const override
return new IteratorDerived(*this);
;
GenericIterator
implementation. This is the actual implementation of GenericIterator
, based on IteratorBase
and IteratorDerived
. Any iterator given to GenericIterator
is wrapped in the corresponding IteratorDerived
, which is then assigned to the IteratorBase
pointer.
template <class Value>
class GenericIterator
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
GenericIterator& operator=(GenericIterator const& it)
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const return *(*iterator);
GenericIterator& operator++()
++(*iterator);
return *this;
void operator++(int)
++(*iterator);
bool operator==(GenericIterator const& other) const
return *iterator == *other.iterator;
bool operator!=(GenericIterator const& other) const
return *iterator != *other.iterator;
;
add a comment |
Use an iterator class that abstracts this funtionality.
An iterator has 3 basic functions:
- Increment
- Dereference
- Check for equality
We can use these as guidelines in creating an interface which abstracts this behavior. This interface is a bit cumbersome to use on it's own, but we can use it to build a wrapper class, GenericIterator
, that can automatically be assigned any other iterator type.
GenericIterator
: A ready-made general purpose solution
It's possible to write a GenericIterator
class that can be assigned iterators from pretty much any collection, including the reverse iterators.
int main()
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values 1, 2, 3 ;
GenericIterator<int&> begin, end;
if(iterate_forward)
begin = values.begin();
end = values.end();
else
begin = values.rbegin();
end = values.rend();
// Print out the values
for(; begin != end; ++begin)
std::cout << *begin << " ";
You can download the entirety of the code from this github repo, which I'll be updating and improving as needed.
Remarks on performance GenericIterator
In terms of functionality, GenericIterator
gives you everything you could possibly ask for. It's light-weight; and it's convenient; and it's easy to re-purpose if your code needs to read from a std::list
or something other than a vector.
However, due to the fundamental limitations of runtime polymorphism, it's significantly more difficult for the compiler to inline the virtual method calls. This means that GenericIterator
carries more runtime overhead than other solutions.
It's a good idea to favor static polymorphism and templates over runtime polymorphism when possible. If you're able to do so, use something like Mark B's solution, which will ultimately be more performant.
Appendix
Iterator interface definition. This class is used to implement GenericIterator
. A GenericIterator
contains a pointer to IteratorBase
, which is used to achieve the runtime polymorphism. Thanks to the clone()
method, GenericIterator
remains both copyable and movable as expected.
template <class Value>
class IteratorBase
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
;
Concrete class implementing IteratorBase
. This class implements the behavior defined in IteratorBase
. The iterator it contains is the actual iterator returned by the collection you're iterating over. In your case, it'd be either std::vector::iterator
or std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it)
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override return *it;
IteratorBase<Value>& operator++() override
++it;
return *this;
bool operator!=(IteratorBase<Value> const& other) const override
bool operator==(IteratorBase<Value> const& other) const override
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
IteratorBase<Value>* clone() const override
return new IteratorDerived(*this);
;
GenericIterator
implementation. This is the actual implementation of GenericIterator
, based on IteratorBase
and IteratorDerived
. Any iterator given to GenericIterator
is wrapped in the corresponding IteratorDerived
, which is then assigned to the IteratorBase
pointer.
template <class Value>
class GenericIterator
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
GenericIterator& operator=(GenericIterator const& it)
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const return *(*iterator);
GenericIterator& operator++()
++(*iterator);
return *this;
void operator++(int)
++(*iterator);
bool operator==(GenericIterator const& other) const
return *iterator == *other.iterator;
bool operator!=(GenericIterator const& other) const
return *iterator != *other.iterator;
;
Use an iterator class that abstracts this funtionality.
An iterator has 3 basic functions:
- Increment
- Dereference
- Check for equality
We can use these as guidelines in creating an interface which abstracts this behavior. This interface is a bit cumbersome to use on it's own, but we can use it to build a wrapper class, GenericIterator
, that can automatically be assigned any other iterator type.
GenericIterator
: A ready-made general purpose solution
It's possible to write a GenericIterator
class that can be assigned iterators from pretty much any collection, including the reverse iterators.
int main()
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values 1, 2, 3 ;
GenericIterator<int&> begin, end;
if(iterate_forward)
begin = values.begin();
end = values.end();
else
begin = values.rbegin();
end = values.rend();
// Print out the values
for(; begin != end; ++begin)
std::cout << *begin << " ";
You can download the entirety of the code from this github repo, which I'll be updating and improving as needed.
Remarks on performance GenericIterator
In terms of functionality, GenericIterator
gives you everything you could possibly ask for. It's light-weight; and it's convenient; and it's easy to re-purpose if your code needs to read from a std::list
or something other than a vector.
However, due to the fundamental limitations of runtime polymorphism, it's significantly more difficult for the compiler to inline the virtual method calls. This means that GenericIterator
carries more runtime overhead than other solutions.
It's a good idea to favor static polymorphism and templates over runtime polymorphism when possible. If you're able to do so, use something like Mark B's solution, which will ultimately be more performant.
Appendix
Iterator interface definition. This class is used to implement GenericIterator
. A GenericIterator
contains a pointer to IteratorBase
, which is used to achieve the runtime polymorphism. Thanks to the clone()
method, GenericIterator
remains both copyable and movable as expected.
template <class Value>
class IteratorBase
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
;
Concrete class implementing IteratorBase
. This class implements the behavior defined in IteratorBase
. The iterator it contains is the actual iterator returned by the collection you're iterating over. In your case, it'd be either std::vector::iterator
or std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it)
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override return *it;
IteratorBase<Value>& operator++() override
++it;
return *this;
bool operator!=(IteratorBase<Value> const& other) const override
bool operator==(IteratorBase<Value> const& other) const override
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
IteratorBase<Value>* clone() const override
return new IteratorDerived(*this);
;
GenericIterator
implementation. This is the actual implementation of GenericIterator
, based on IteratorBase
and IteratorDerived
. Any iterator given to GenericIterator
is wrapped in the corresponding IteratorDerived
, which is then assigned to the IteratorBase
pointer.
template <class Value>
class GenericIterator
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
GenericIterator& operator=(GenericIterator const& it)
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const return *(*iterator);
GenericIterator& operator++()
++(*iterator);
return *this;
void operator++(int)
++(*iterator);
bool operator==(GenericIterator const& other) const
return *iterator == *other.iterator;
bool operator!=(GenericIterator const& other) const
return *iterator != *other.iterator;
;
edited May 14 at 21:52
answered May 14 at 21:20
J. Antonio PerezJ. Antonio Perez
4,781926
4,781926
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56133546%2fc-conditional-uni-directional-iterator%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
May be, write a wrapper which contains both iterators (forward and reverse) and a flag which remembers which one is currently used. Additionally, you have to overload at least
operator++()
andoperator!=()
.– Scheff
May 14 at 15:09
Does some action use/store the iterator apart from dereferencing?
– Quimby
May 14 at 15:12
Is
bar
a compile-time constant?– YSC
May 14 at 15:12
@Scheff: Definitely possible, but lots of boilerplate. Even in C++20 (and that's an incomplete impl)
– AndyG
May 14 at 19:23