patterncppModerate
Making C++11 range-based for loops a bit more useful
Viewed 0 times
bitrangeloopsmoreforbasedusefulmaking
Problem
C++11 is great. Probably one of the most beautiful features (in my opinion) is the so-called range-based-for-loop.
Instead of
or
or simplified with C++11 auto:
we can say this:
This is very expressive: We talk about the entry, rather than the i^th position in the range or the iterator pointing to an entry. However, this syntax lacks the ability of having subranges (e.g. ignoring the last entry). In a series of small helper structs / methods, I want to add this functionality in a clean way.
So much for my motivation ;-) Now, for the real deal.
In this post, I address conditions on the entries of
but without that level of indentation.
Here is the code for ConditionalRange:
```
template
ConditionalRange makeConditionalRange(Range& range, Runnable&& condition)
{
static_assert(std::is_same().begin())), bool>::value, "Condition must return a boolean value.");
return ConditionalRange(range, std::forward(condition));
}
template
struct ConditionalRange
{
public:
friend ConditionalRange makeConditionalRange<>(Range&, Runnable&&);
public:
using iterator_type = ConditionalIterator().begin()), Runnable>;
iterator_type begin() const
{
auto b = range.begin();
while ( b != range.end() && !condition(*b) )
{
++b;
}
return ConditionalIterator().begin()), Runnable>(b, range.end(), condition);
}
iterator_type end() const
{
Instead of
for ( std::size_t i(0); i < range.size(); ++i )
{
// do something to range[i];
}or
for ( Range::iterator it(range.begin()); it != range.end(); ++it )
{
// do something to *it;
}or simplified with C++11 auto:
for ( auto it(range.begin()); it != range.end(); ++it )
{
// do something to *it;
}we can say this:
for ( auto& entry : range )
{
// do something with entry.
}This is very expressive: We talk about the entry, rather than the i^th position in the range or the iterator pointing to an entry. However, this syntax lacks the ability of having subranges (e.g. ignoring the last entry). In a series of small helper structs / methods, I want to add this functionality in a clean way.
So much for my motivation ;-) Now, for the real deal.
In this post, I address conditions on the entries of
range. Basically, the helper code should perform the equivalent offor ( auto& entry : range )
{
if ( condition(entry) )
{
// do something to entry.
}
}but without that level of indentation.
Here is the code for ConditionalRange:
```
template
ConditionalRange makeConditionalRange(Range& range, Runnable&& condition)
{
static_assert(std::is_same().begin())), bool>::value, "Condition must return a boolean value.");
return ConditionalRange(range, std::forward(condition));
}
template
struct ConditionalRange
{
public:
friend ConditionalRange makeConditionalRange<>(Range&, Runnable&&);
public:
using iterator_type = ConditionalIterator().begin()), Runnable>;
iterator_type begin() const
{
auto b = range.begin();
while ( b != range.end() && !condition(*b) )
{
++b;
}
return ConditionalIterator().begin()), Runnable>(b, range.end(), condition);
}
iterator_type end() const
{
Solution
You can use braces to return from your functions. That way, you won't have to repeat long and cumbersome types (DRY, as much as possible). For example,
Also, you probably want your function to accept C-style arrays too. Therefore, you should use
With that (everywhere in your code) and some trivial changes, your code will also work for C-style arrays (tested here and it works fine).
Naming your function
The name
ConditionalRange::end:iterator_type end() const
{
return { range.end(), range.end(), condition };
}Also, you probably want your function to accept C-style arrays too. Therefore, you should use
std::begin and std::end instead of calling the member functions:iterator_type end() const
{
return { std::end(range), std::end(range), condition };
}With that (everywhere in your code) and some trivial changes, your code will also work for C-style arrays (tested here and it works fine).
Naming your function
The name
makeConditionalRange is quite long. The function you created already exists in other libraries and programming languages (Python comes to my mind) under the name filter. You should consider changing its name in order for many users to recognize the name at first glance (moreover, it will be shorter).Code Snippets
iterator_type end() const
{
return { range.end(), range.end(), condition };
}iterator_type end() const
{
return { std::end(range), std::end(range), condition };
}Context
StackExchange Code Review Q#43308, answer score: 10
Revisions (0)
No revisions yet.