Is there an equivalent to the "for ... else" Python loop in C++?
Solution 1
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();
Solution 2
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:
...
Solution 3
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();
Solution 4
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.
Solution 5
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();
Comments
-
Delgan over 3 years
Python has an interesting
for
statement which lets you specify anelse
clause.In a construct like this one:
for i in foo: if bar(i): break else: baz()
the
else
clause is executed after thefor
, but only if thefor
terminates normally (not by abreak
).I wondered if there was an equivalent in C++? Can I use
for ... else
? -
Admin almost 10 yearsNote that this counts up from 0 to foo while the Python loop goes through the elements of the iterable foo. Probably easy to adjust though (compare to
end()
). -
Admin almost 10 yearsNo. The Python loop doesn't count at all.
i
probably never is an integer. It's more like C++11for (auto i : foo)
. -
m.wasowski almost 10 years
for(auto it = begin(something); it != end(something); ++i)
... if (it == end(something))`... Also please remember, that everytime you do postfix incrementation unecessary, a kitten dies. -
Easton almost 10 yearsWhoops, fixed the issue.
-
Admin almost 10 yearsThe
break
is fine IMHO, and in any case that change is completely orthogonal to the actual meat (it != foo.end()
). -
Thijs van Dien almost 10 years+1 because I think this solution most clearly states the intent.
-
ifyalciner almost 10 years@MatthiasB As far as I understand question is not about what to use, instead it is about the possible implementations in C++.
-
ifyalciner almost 10 years@MatthiasB And also I don't think that 'goto' is outdated. Because there are still fields where you need to omit extra 'if' checks and declarations for performance and memory considerations, while still programming in C++.
-
Jarod42 almost 10 yearsYou may add your own exception, else you badly catch exception from
bar(i)
andbaz()
-
ifyalciner almost 10 years@Jarod42 you are right. Custom exception would be better.
-
ifyalciner almost 10 years@MatthiasB I think this will be a good example. Actual impact of the thread actually about emptying pipeline because of the false prediction in the case. But still a branch can effect terribly. Especially if you are repeating many times which i don't see reason the neglect many repetitions because domain is absent. Also I use C++ in embedded systems. You are right that it is not a good practice but still i believe is a good exception.
-
Admin almost 10 years@Faruxx I'm afraid you'll have to elaborate. There is not a single mention of "goto" in that question, and the subject matter (branch prediction) doesn't lend itself to any obvious examples. The issue is an unpredictable jump in the machine code, a
goto
also produces jump instructions and does not alter predictability. -
ifyalciner almost 10 years@delnan Sorry I couldn't be clear. My point was that by using
goto
you can eliminate a branch. And in the link if you check the answers will see that one solution to branch prediction is removing the branch altogether. But still I see no more necessity of the arguing on the matter. Thanks for all responses. -
Admin almost 10 years@Faruxx A
goto
is a branch (possibly an unconditional one). The only instance of removing branches code I could find in the answers doesn't usegoto
. If you want to argue thatgoto
allows replacing conditional branches with unconditional ones, please give an example because so far it sounds like cargo cult. -
ifyalciner almost 10 years@delnan in the most voted answer; here :
int t = (data[c] - 128) >> 31; sum += ~t & data[c];
. Of coursegoto
is a branch such asbreak
. I have removed second branch which is a conditional one that you can see in other answers. -
Admin almost 10 years@Faruxx Okay, missed that one, but it doesn't need or use
goto
either. I'm aware of branch prediction and its effects and no not dispute them. What I'm asking is howgoto
helps avoid branch mispredictions. -
ifyalciner almost 10 years@delnan No no! I never meant that. It helps only to remove branch removal where theoretically you are removing branch correct or mis prediction. The reason i have given that example is this code block may repeat many times, one more
if
check will increase the chance of branch mis-prediction (as well as correct prediction). In some cases where code optimization is importantgoto
is still a good practice for experienced ones. @MatthiasB is right where you are coding for an user application in big project with many people that understandably of code is more important than optimization. -
James Kanze almost 10 years@BenVoigt Good point. That's what comes of typing too quickly. Should be a
while
; I'll fix it. -
Joeytje50 over 9 years@m.wasowski could you please explain why
i++
would be so bad in a forloop? It's got exactly the same functionality as++i
there. I personally thinki++
looks better, because it's got the part that changes the variable after the variable name itself, just like where+=1
comes afteri
in the normali+=1
, etc. In cases where it doesn't matter to the outcome of the code, I always usei++
. -
m.wasowski over 9 years@Joeytje50
i++
can be significantly slower, as it involves creating a new object; it is a bad habit to use it and you won't find it in decent code in place where++i
can be used instead. Personally, it forces me unecessarilly to slow down and think if there is was a reason to use it, or is it just sloppy code. Even if for a second, it might be annoying. -
Joeytje50 over 9 years@m.wasowski this guy's tests say otherwise. I get why
i++
could be slower in some situations, but in a forloop it makes no difference if you've got a modern enough compiler (which is what I'm taking from that SO link), so I would still preferi++
because of those things I said. So, to fix your original statement: "Everytime you do postfix incrementation unnecessarily, your compiler turns it into exactly the same as prefix incrementation." -
m.wasowski over 9 years@Joeytje50 mind that this is answer for a different programming language. For example, iterators are not primitive types like
int
. Also see comments below, you will find all the reasons why++i
should be used as default. And IMHO, for sake of consistency, the same should be used for primitive types, likeint
. -
Андрей Беньковский over 8 yearsYou don't need a semicolon after the label. The context implies there are operators after this code (otherwise one could just use an early return).
-
Андрей Беньковский over 8 yearsThe goto part isn't that bad. That's what I'd use in this situation. I think it is actually more readable than the flag-based solution.
-
Андрей Беньковский over 8 yearsSacrificing readability to save 5 seconds of typing is usually a very bad idea.
-
srdjan.veljkovic over 8 yearsWell, readability is subjective, while typing isn't. It's a trade-off, like everything else. I'm not advocating its use, just showing how you can achieve a "for each...else" in C++ without goto (which has its own problems).
-
ifyalciner over 8 years@АндрейБеньковский Thanks for suggestions and mentioning.
-
0kcats over 8 yearsA goto pattern here is much, much better than declaring a macro.
-
0kcats over 8 yearsIf we consider language equivalency, then this is the only valid answer. Python does not have gotos, while C++ has, and all other examples, such as using return, a temporary variable and break, or exceptions can be done with Python. Plus, this case (for else) as well as breaking out of a nested loop are probably the two only cases of valid applications of goto in C++.
-
kamikaze over 8 yearsgoto is well defined and often the most comprehensible (i.e. best maintainable) solution for complex loop handling. This solution has my vote (I would have given a similar one).
-
0kcats over 8 yearsNot completely equivalent. For example, it does not allow modification of the elements of the container by the "bar". Not to mention, that it made me (a C++ dev) go look it up in the help, while the the original OP python sample I had no problem understanding immediately. The stl turns into a mud pile.
-
0kcats over 8 yearsI was referring to the help page here: cplusplus.com/reference/algorithm/none_of or here en.cppreference.com/w/cpp/algorithm/all_any_none_of. Both say that the predicate shall not modify the argument.
-
Tony Delroy over 8 years@0kcats: hmmm... 25.1/8 does say "The function object pred shall not apply any non-constant function through the dereferenced iterator.", so you're right. Interesting! Related discussion here. Cheers
-
Андрей Беньковский over 8 yearsThe first example can be replaced with an if statement using python's built-ins
any
andmap
or an iterator comprehension. -
Tony Delroy over 8 years@agomcas: this is very interesting, as is the contrast with equivalent C++, e.g.:
for (int retrynum = 0; ; ++retrynum) { try { attempt_operation(); } catch (const SomeException&) { if (retrynum == max_retries) throw ...; } }
. Not sure which I prefer. -
agomcas over 8 years@АндрейБеньковский but I believe with the any() construction there is not way to recover which element fulfilled the condition. In the for...else case you have it stored in item and may do something else with it.
-
Андрей Беньковский over 8 years@agomcas I continued my experiments. link. I'm not sure which of two solutions is more readable.
-
Андрей Беньковский over 8 years@agomcas A bit more improved version: melpon.org/wandbox/permlink/xiQBiPYY9zBV0ceY
-
agomcas over 8 years@АндрейБеньковский For me, readability of the for... else is better by far. The other option involves creation of a separate sentinel object + using the generator returned from filter + using next with a default return (first time I ever encountered this usage BTW). It goes from a construction that you can explain in an introduction to programming right just after 'if...else' and 'for' to a construction that needs quite some knowledge of python (plus I bet is more expensive in resources), just to match the same amount of lines. Not really a contender?
-
Tony Delroy over 8 years@agmos: "with the
any()
construction there is not way to recover which element fulfilled the condition" - that's part of the self-documenting beauty of it - the code reader knows up front that it doesn't matter which element matches wheneverany
is used, whilefind_if
implies it may matter. Good programmers will pick accordingly. -
ferdynator over 8 yearsWhy
i > foo
? Shouldn't it bei < foo
? -
Yam Marcovic about 8 yearsWhy would it require specifying the type?
-
arekolek about 8 years@YamMarcovic Admittedly, I wasn't aware you can use auto specifier in lambdas. But it works only in C++14 and I was using C++11.
-
nonsensickle over 7 yearsI'm not sure how I feel about the macro leaving behind a new variable in my scope. In your example an iterator
x
will be left behind after the else statement. There isn't a nice way to get around this one... -
srdjan.veljkovic over 7 yearsI added an example of a way to avoid bringing a new variable into current scope. I'm not saying it's nice, but, it works. :)
-
Matej Kolec'ko over 4 yearsI think using goto is antipattern nowadays, but if you do it on this micro place I think you are okay, but I think this should be last thing to consider to use.
-
12431234123412341234123 almost 3 years@MatthiasB Can you explain which construct he uses that you think is outdated?
-
12431234123412341234123 almost 3 years@MatthiasB This answer is a prime example why
goto
isn't outdated in C.