Is there an equivalent to the "for ... else" Python loop in C++?

Python has an interesting for statement which lets you specify an else clause.

In a construct like this one:

for i in foo:
if bar(i):
break
else:
baz()

the else clause is executed after the for, but only if the for terminates normally (not by a break).

I wondered if there was an equivalent in C++? Can I use for ... else?

32211 次浏览

This is my rough implementation in C++:

bool other = true;
for (int i = 0; i > foo; i++) {
if (bar[i] == 7) {
other = false;
break;
}
} if(other)
baz();

Yes you can achieve the same effect by:

auto it = std::begin(foo);
for (; it != std::end(foo); ++it)
if(bar(*it))
break;
if(it == std::end(foo))
baz();

Something like:

auto it = foo.begin(), end = foo.end();
while ( it != end && ! bar( *it ) ) {
++ it;
}
if ( it != foo.end() ) {
baz();
}

should do the trick, and it avoids the unstructured break.

If doesn't mind using goto also can be done in following way. This one saves from extra if check and higher scope variable declaration.

for(int i = 0; i < foo; i++)
if(bar(i))
goto m_label;
baz();


m_label:
...

A simpler way to express your actual logic is with std::none_of:

if (std::none_of(std::begin(foo), std::end(foo), bar))
baz();

If the range proposal for C++17 gets accepted, hopefully this will simplify to:

if (std::none_of(foo, bar)) baz();

There is no such language construct in C++, but, thanks to the "magic" of the preprocessor, you can make one for yourself. For example, something like this (C++11):

#include <vector>
#include <iostream>
using namespace std;


#define FOR_EACH(e, c, b) auto e = c.begin(); for (; e != c.end(); ++e) {b} if (e == c.end()) {}


int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);


FOR_EACH(x, v, {
if (*x == 2) {
break;
}
cout << "x = " << *x << " ";
})
else {
cout << "else";
}


return 0;
}

This should output x = 1 else.

If you change if (*x == 2) { to if (*x == 3) {, the output should be x = 1 x = 2.

If you don't like the fact that a variable is added in the current scope, you can change it slightly:

#define FOR_EACH(e, c, b, otherwise) {auto e = c.begin(); for (; e != c.end(); ++e) {b} if (e == c.end()) {} otherwise }

then use would be:

FOR_EACH(x, v, {
if (*x == 2) {
break;
}
cout << "x = " << *x << " ";
},
else {
cout << "else";
})

It's not perfect, of course, but, if used with care, will save you some amount of typing and, if used a lot, would become a part of the project's "vocabulary".

I am not aware of an elegant way to accomplish this in C/C++ (not involving a flag variable). The suggested other options are much more horrible than that...

To answer @Kerrek SB about real life usages, I found a few in my code (simplified snippets)

Example 1: typical find/fail

for item in elements:
if condition(item):
do_stuff(item)
break
else: #for else
raise Exception("No valid item in elements")

Example 2: limited number of attempts

for retrynum in range(max_retries):
try:
attempt_operation()
except SomeException:
continue
else:
break
else: #for else
raise Exception("Operation failed {} times".format(max_retries))

You could use a lambda function for this:

[&](){
for (auto i : foo) {
if (bar(i)) {
// early return, to skip the "else:" section.
return;
}
}
// foo is exhausted, with no item satisfying bar(). i.e., "else:"
baz();
}();

This should behave exactly like Python's "for..else", and it has some advantages over the other solutions:

  • It's a true drop-in replacement for "for..else": the "for" section can have side effects (unlike none_of, whose predicate must not modify its argument), and it has access to the outer scope.
  • It's more readable than defining a special macro.
  • It doesn't require any special flag variables.

But... I'd use the clunky flag variable, myself.

There probably isn't a single solution that fits best all problems. In my case a flag variable and a range-based for loop with an auto specifier worked best. Here's an equivalent of the code in question:

bool none = true;
for (auto i : foo) {
if (bar(i)) {
none = false;
break;
}
}
if (none) baz();

It is less typing than using iterators. Especially, if you use the for loop to initialize a variable, you may use that instead of the boolean flag.

Thanks to auto typing it is better than std::none_of, if you want to inline the condition rather than call bar() (and if you are not using C++14).

I had a situation where both conditions occurred, the code looked something like this:

for (auto l1 : leaves) {
for (auto x : vertices) {
int l2 = -1, y;
for (auto e : support_edges[x]) {
if (e.first != l1 && e.second != l1 && e.second != x) {
std::tie(l2, y) = e;
break;
}
}
if (l2 == -1) continue;


// Do stuff using vertices l1, l2, x and y
}
}

No need for iterators here, because v indicates whether break occurred.

Using std::none_of would require specifying the type of support_edges[x] elements explicitly in arguments of a lambda expression.

It's not only possible in C++, it's possible in C. I'll stick with C++ to make the code comprehensible though:

for (i=foo.first(); i != NULL || (baz(),0); i = i.next())
{
if bar(i):
break;
}

I doubt I'd let that through a code review, but it works and it's efficient. To my mind it's also clearer than some of the other suggestions.

Direct answer: no, you probably can't, or it is compiler-based, at best. BUT here's a hack of a macro that kind of works!

A few notes:

I usually program with Qt, so I'm used to having a foreach loop, and never have to deal with iterators directly.

I tested this with Qt's compiler (v 5.4.2) but it should work. This is gross for several reasons, but generally does what you'd want. I don't condone coding like this, but there's no reason it shouldn't work as long as you're careful with the syntax.

#include <iostream>
#include <vector>


#define for_else(x, y) __broke__ = false; for(x){y} if (__broke__) {}
#define __break__ __broke__ = true; break


bool __broke__;  // A global... wah wah.


class Bacon {
public:
Bacon(bool eggs);


inline bool Eggs() {return eggs_;}


private:
bool eggs_;
};


Bacon::Bacon(bool eggs) {
eggs_ = eggs;
}


bool bar(Bacon *bacon) {
return bacon->Eggs();
}


void baz() {
std::cout << "called baz\n";
}


int main()
{
std::vector<Bacon *>bacons;


bacons.push_back(new Bacon(false));
bacons.push_back(new Bacon(false));
bacons.push_back(new Bacon(false));


for_else (uint i = 0; i < bacons.size(); i++,
std::cout << bacons.at(i)->Eggs();
if (bar(bacons.at(i))) {
__break__;
}
) else {
baz();
}


bacons.push_back(new Bacon(true));
bacons.push_back(new Bacon(false));


for_else (uint i = 0; i < bacons.size(); i++,
std::cout << bacons.at(i)->Eggs();
if (bar(bacons.at(i))) {
__break__;
}
) else {
baz();
}


return EXIT_SUCCESS;
}

You can use for-else almost like in Python by defining two macros:

#define BREAK {CONTINUETOELSE = false; break;}
#define FORWITHELSE(x, y) {bool CONTINUETOELSE = true; x if(!CONTINUETOELSE){} y}

Now you put the for and the else inside the FORWITHELSE macro separated by a comma and use BREAK instead of break. Here is an example:

FORWITHELSE(
for(int i = 0; i < foo; i++){
if(bar(i)){
BREAK;
}
},
else{
baz();
}
)

There are two things you need to remember: to put a comma before the else and to use BREAK instead of break.

I came here because I had the same question, in C though. The best thing I came out with is

bool notTerminated = true;
for (int i = 0; i < 50 || (notTerminated = false); i++)
if (bar(i))
break;
if (! notTerminated)
baz();

Explanation: the (notTerminated = false) is an assignment that will always return the false value, it will never affect the condition and will be evaluated iif the condition if true.

I would accomplish this with a simple helper variable:

#include <stdio.h>
#include <stdbool.h>


int main()


{
bool b;
printf("Numbers which are multiples of 7:\n");


for (int i=8; b=(i<12); i++)
{
if (i%7==0)
{
printf("%d", i);
break;
}
}
if (!b)
{
printf("no numbers found\n");
}
return 0;
}

This way, you need to implement the condition (in the above examplei<12) only at one place.