7

In some code I was working on, I have a for loop that iterates through a map:

for (auto it = map.begin(); it != map.end(); ++it) {

    //do stuff here
}

And I wondered if there was some way to concisely write something to the effect of:

for (auto it = map.begin(); it != map.end(); ++it) {
    //do stuff here
} else {
    //Do something here since it was already equal to map.end()
}

I know I could rewrite as:

auto it = map.begin();
if (it != map.end(){

    while ( it != map.end() ){
        //do stuff here
        ++it;
    }

} else {
    //stuff
}

But is there a better way that doesn't involve wrapping in an if statement?

Wolf
  • 9,679
  • 7
  • 62
  • 108
m33p
  • 91
  • 1
  • 4

3 Answers3

22

Obviously...

if (map.empty())
{
    // do stuff if map is empty
}
else for (auto it = map.begin(); it != map.end(); ++it)
{
    // do iteration on stuff if it is not
}

By the way, since we are talking C++11 here, you can use this syntax:

if (map.empty())
{
    // do stuff if map is empty
}
else for (auto it : map)
{
    // do iteration on stuff if it is not
}
Havenard
  • 27,022
  • 5
  • 36
  • 62
  • 1
    In general though, say for an object that doesn't have .empty(), is there a more concise way to for-else? – m33p Aug 08 '14 at 17:01
  • 2
    `if (map.begin() == map.end())` – Havenard Aug 08 '14 at 17:01
  • 1
    `templatebool empty(R const& r){using std::begin; using std::end; return begin(r)==end(r);}` is a `.empty()` that works on all iterable ranges. – Yakk - Adam Nevraumont Aug 08 '14 at 17:13
  • 3
    Is this `else for`-condensed-into-one-line actually familiar to people? I mean sure it's clever and accomplishes its purpose, but I can't help but wonder if it's recognized enough to be as readable as an indented `for` on the next line. – chris Aug 08 '14 at 17:23
  • @chris It's not idiomatic the way writing `} else if (...) {` in place of `} else { if (...) {` is, but it shouldn't confuse anyone who's vaguely familiar with C-like languages. On the other hand clang-format doesn't automatically format code this way so unless one wants to format code manually... – bames53 Aug 08 '14 at 17:35
  • 2
    This isn't the same as a Python `else` since that executes if the container is empty, or it has been traversed through entirely. The only time it doesn't execute is on a `break` statement. See: https://docs.python.org/3.4/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops – Kevin Anderson Aug 08 '14 at 18:47
  • @KevinAnderson the question doesn't explicitly mention python, the intended behaviour is placed in a comment `//Do something here since it was already equal to map.end()` – Wolf Nov 28 '14 at 10:51
  • @Wolf - True, but the original author mentions Python in a comment below his original question, and thus it's up to mention. I do agree his original comment in the code is the most important. – Kevin Anderson Nov 28 '14 at 15:35
  • Better would be `for (auto &&it : map)`: the way you have it, the map entry is copied by value to `it` for each loop iteration. In a range-based for loop, `it` is not actually an iterator; it's the value stored in the container. There is no way to access the underlying iterator. – M.M Dec 01 '14 at 00:03
  • You answer is quite impressive (as I already mentioned in SOspeak), it lead me to the [even more astonishing if-for-else control structure](http://stackoverflow.com/a/27187956/2932052) that is even valid C! But most programmers seem to be really shocked by such structural options. – Wolf Dec 06 '14 at 15:37
5

If you want more crazy control flow in C++, you can write it in C++11:

template<class R>bool empty(R const& r)
{
  using std::begin; using std::end;
  return begin(r)==end(r);
}
template<class Container, class Body, class Else>
void for_else( Container&& c, Body&& b, Else&& e ) {
  if (empty(c)) std::forward<Else>(e)();
  else for ( auto&& i : std::forward<Container>(c) )
    b(std::forward<decltype(i)>(i));
}

for_else( map, [&](auto&& i) {
  // loop body
}, [&]{
  // else body
});

but I'd advise against it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Inspired by Havenard's else for, I tried this structure with the else part sitting in the right place [1].

if (!items.empty()) for (auto i: items) {
    cout << i << endl;
} else {
    cout << "else" << endl;
}

(full demo)

I'm not sure if I would use it in real code, also because I do not remember a single case that I was missing an else clause for the for loop, but I've to admit that only today I learned that python has it. I read from your comment

//Do something here since it was already equal to map.end()

...that you are probably not referring to python's for-else, but maybe you did - also python programmers seem to have their problems with this feature.


[1] unfortunately, there is no concise opposite of is empty in C++ ;-)

Community
  • 1
  • 1
Wolf
  • 9,679
  • 7
  • 62
  • 108