C++17 has selection statements with initializer
status_code foo() {
if (status_code c = bar(); c != SUCCESS) {
return c;
}
// ...
}
I'd like to write a while-loop and a variable with a scope limited to the loop and initialized only once before the first iteration.
// fake example, doesn't compile, is doable in many ways
while (bool keep_trying = foo(); keep_trying) {
// do stuff
if (something)
keep_trying = false;
}
Is there anything for this in C++17 or maybe coming in C++2a?
P0305R1, the paper that introduced the if statement with initialization, explains this pretty well. From the Proposal section:
There are three statements in C++, if, for and while, which are all
variations on a theme. We propose to make the picture more complete by
adding a new form of if statement.
while (cond) E;
for (init; cond; inc) E;
if (cond) E;
if (cond) E; else F;
if (init; cond) E; (new!)
if (init; cond) E; else F; (new!)
(table simplified)
Note that while (cond) corresponds to for (init; cond; inc). Also, from the Discussion section:
It is often said that C++ is already complex enough, and any
additional complexity needs to be carefully justified. We believe that
the proposed extension is natural and unsurprising, and thus adds
minimal complexity, and perhaps even removes some of the existing
differences among the various control flow statements. There is
nothing about the local initialization that is specific to loop
statements, so having it only on the loop and not on the selection
statement seems arbitrary. Had the initializer form of the if
statement been in the language from the start, it would not have
seemed out of place. (At best one might have wondered why for is not
also spelled while, or vice versa.)
"While statement with initializer" = "For statement without updation"
And you have always had a for loop regardless of the version of the language.
Related
Am reading through (the) C++ Core guidelines and encountered this rule: "Don’t declare a variable until you have a value to initialize it with" https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es22-dont-declare-a-variable-until-you-have-a-value-to-initialize-it-with
It describes the following code as bad:
SomeLargeType var;
if (cond) // some non-trivial condition
Set(&var);
else if (cond2 || !cond3) {
var = Set2(3.14);
}
else {
var = 0;
for (auto& e : something)
var += e;
}
Unfortunately this point fails to describe a way to how to solve this exact issue. Sometimes you just have to initialize a large object differently depending on a condition.
The only circumvent that comes to my mind is something like:
SomeLargeType * var;
if (cond) // some non-trivial condition
var = new SomeLargeType(123);
else if (cond2 || !cond3) {
var = new SomeLargeType(3.14);
}
However even if I use a smartpointer, this feels somehow unnecessary/unsafe and most of all, worse than the initial way.
What is the optimal solution?
You can use a function. Also, don't use bare pointers with ownership (I assume that there's a guideline for this too). Example:
std::unique_ptr<SomeLargeType>
make_something(bool cond, bool cond23)
{
if (cond)
return std::make_unique<SomeLargeType>(123);
else if (cond23)
return std::make_unique<SomeLargeType>(3.14);
else
return nullptr;
}
// usage
std::unique_ptr<SomeLargeType> var = make_something(cond, cond2 || !cond3);
If there's no way for this function to be reusable, then a lambda might be appropriate, as shown by Sopel
First, there is no undefined behaviour in the original sample code: all possible conditions are taken care of (by the final else block). However, there is potential undefined behaviour in the answers so far given, as they replace the 'good' code in the aforementioned else block with, effectively, no initialisation and the return of a nullptr, which may later be the subject of an attempted dereference.
Also, there is no real need here to complicate matters by replacing the instance variable with a pointer (and this also changes the nature/logic of the code).
Using a lambda (as suggested in the link provided by StaceyGirl) is certainly a good way to go (probably the best, but that may be subjective). However, to keep the logic the same as the original code, one can apply the lambda to the object, rather than to a pointer, as so:
SomeLargeType var = [&]() {
if (cond) { // some non-trivial condition
SomeLargeType v1;
Set(&v1);
return v1;
}
else if (cond2 || !cond3) {
SomeLargeType v2 = Set2(3.14);
return v2;
}
else {
SomeLargeType v3 = 0;
for (auto& e : something) var += e;
return v3;
}
}();
Here, unlike in the original code (where a default constructor is first called, then one of three others), a constructor for SomeLargeObject will only be called once¹ and there will be no undefined behaviour. It is this initial call to the (potentially very expensive) default constructor that, I presume, is the reason for this being cited as an example of "bad code."
¹ If there are any doubts about how often the constructors are called, I can provide a complete MCVE (with some minor modifications to avoid the undefined for (auto& e : something) line), if such is requested.
An immediately invoked lambda can be used as an alternative to a named function.
SomeLargeType* var = [&]() {
if (cond) // some non-trivial condition
return new SomeLargeType(123);
else if (cond2 || !cond3)
return new SomeLargeType(3.14);
else
return nullptr;
}();
Note that some types may not provide a default constructor, in which case it's impossible to have a lazy initialization without using a boxing type.
This can also improve performance when the type is not trivially constructible.
https://godbolt.org/z/_V8t2T
In C and in javascript I enjoy the ability to write this kind of thing and have it just work.
while (a)
{
ctx: while(b)
{
while (c)
{
if(d) break ctx;
...
}
}
...
}
Perhaps I'm just confused about C++ versions but I get this kind of error in g++:
error: expected ‘;’ before ‘ctx’
break ctx;
error: ‘ctx’ was not declared in this scope
warning: label ‘ctx’ defined but not used [-Wunused-label]
ctx:
C++ appears to refuse letting me write this code.
C++ has added lambdas/closures that potentially would let me do this but I'm not quite sure how they would work in this case.
Using try throw catch is the closest construct I can think of that produces this behavior but the sloppiness of using an error system when none should be needed concerns me (Also they are slow I hear).
I'm tempted to just wrap it in extern C except I'm relying on c++ library's completely for the entire project so this also feels sloppy.
Is a try block or just rewriting my only options?
Neither C nor C++ have a labelled break statement (You're probably using a language extension, rather than standard C).
Instead, you can use goto to break out of a nested loop.
while (a)
{
while(b)
{
while (c)
{
if(d)
goto break_b;
}
}
break_b:
// ...
}
I was able to use goto to solve this... I though it was a banned construct in c++?
No. goto is not "banned" in C++.
This is a completely fine way to use goto. There doesn't exist an equivalent structured control statement.
lambdas/closures [...] potentially would let me do this but I'm not quite sure how they would work in this case.
If you are allergic to goto, then you can indeed use a lambda, but I don't see it providing any additional readability:
while (a)
{
[&](){
while(b)
{
while (c)
{
if(d)
return;
}
}
}();
// ...
}
Instead of a lambda, you can use a named function. But in that case you need to pass any variables (such as b, c and d) as arguments (assuming they're not globals).
Yet another way is an extra variable:
while (a)
{
bool break_b = false;
while(b)
{
while (c)
{
if(d) {
break_b = true;
break;
}
}
if (break_b)
break;
}
// ...
}
Of these three methods, I recommend goto, since it's the most readable. Except in the case the actual inner loop omitted from the example is very long, in which case a separate function might be better.
As has already been pointed out by others, goto would be a way to do exactly what you're asking for.
That being said, I would argue that, before asking the question of how to break out of a massively-complicated control flow structure, you should first ask yourself why there is a massively-complicated flow structure to begin with. What is going on in these loops? Should whatever is going on in each of these loops not better be moved into its own function? For example, instead of
while (a)
{
ctx: while (b)
{
while (c)
{
if (d) goto ctx;
…
}
}
…
}
why not
bool doC()
{
while (c)
{
if (d)
return false;
…
}
return true;
}
void doB()
{
while (b && doC());
}
and then
while (a)
{
doB();
…
}
Replacing the break with a goto here is not advisable. There can be issues wrt constructors and destructors not being called correctly. Whilst goto still exists in C++, it's really not something you want to use unless you really know what you're doing! A safer option would be to use a try-catch block. A better approach would be to re-factor your algorithm (currently it's O(N^3), which should really be ringing some alarm bells!)
while (a)
{
try
{
while(b)
{
while (c)
{
if(d) throw;
}
}
}
catch(...)
{
}
}
Like we all know, it's not that easy to break from a nested loop out of an outer loop without either:
a goto (Example code.)
another condition check in the outer loop (Example code.)
putting both loops in an extra function and returning instead of breaking (Example code.)
Though, you gotta admit, all of those are kinda clumsy. Especially the function version lacks because of the missing context where the loops are called, as you'd need to pass everything you need in the loops as parameters.
Additionally, the second one gets worse for each nested loop.
So, I personally, still consider the goto version to be the cleanest.
Now, thinking all C++0x and stuff, the third option brought me this idea utilizing lambda expressions:
#include <iostream>
bool CheckCondition(){
return true;
}
bool CheckOtherCondition(){
return false;
}
int main(){
[&]{while(CheckCondition()){
for(;;){
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}}();
std::cout << "yep, broke out of it\n";
}
(Example at Ideone.)
This allows for the semantic beauty of a simple return that the third option offers while not suffering from the context problems and being (nearly) as clean as the goto version. It's also even shorter (character-wise) than any of the above options.
Now, I've learned to keep my joy down after finding beautiful (ab)uses of the language, because there's almost always some kind of drawback. Are there any on this one? Or is there even a better approach to the problem?
Please don't do that in a project I'm managing. That's an awkward abuse of lambdas in my opinion.
Use a goto where a goto is useful.
Perfectly valid in my opinion. Though I prefer to assign mine with names, making the code more self documenting, i.e.
int main(){
auto DoThatOneThing = [&]{while(CheckCondition()){
for(;;){
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}};
DoThatOneThing();
std::cout << "yep, broke out of it\n";
}
In which way is that an improvement over
void frgleTheBrgls()
{
while(CheckCondition()) {
for(;;) {
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}
}
int main()
{
frgleTheBrgls();
std::cout << "yep, broke out of it\n";
}
This is much well-known (functions, you know, as in BASIC), clearer (the algorithm's got a nice name explaining what it does), and does exactly the same as yours does.
Especially the function version lacks because of the missing context where the loops are called, as you'd need to pass everything you need in the loops as parameters.
I see that as an advantage. You see exactly what is needed to frgle the brgls. Explicity, when programming, often is a good thing.
One drawback with your proposed syntax: you cannot have more than 2 nested loops. The 'goto' syntax allows this:
int main()
{
for (;;)
{
for (;;)
{
for (;;)
{
if (CheckCondition1()) goto BREAK_ON_COND1;
if (CheckCondition2()) goto BREAK_ON_COND2;
if (CheckCondition3()) break;
// Do stuff when all conditions are false
}
// Do stuff when condition 3 becomes true
}
BREAK_ON_COND2:
// Do stuff when condition 2 becomes true
}
BREAK_ON_COND1: // When condition 1 becomes true
std::cout << "yep, broke out of it\n";
}
While in other statements like if ... else you can avoid braces if there is only one instruction in a block, you cannot do that with try ... catch blocks: the compiler doesn't buy it. For instance:
try
do_something_risky();
catch (...)
std::cerr << "Blast!" << std::endl;
With the code above, g++ simply says it expects a '{' before do_something_risky(). Why this difference of behavior between try ... catch and, say, if ... else ?
Thanks!
Straight from the C++ spec:
try-block:
try compound-statement handler-seq
As you can see, all try-blocks expect a compound-statement. By definition a compound statement is multiple statements wrapped in braces.
Have everything in a compound-statement ensures that a new scope is generated for the try-block. It also makes everything slightly easier to read in my opinion.
You can check it yourself on page 359 of the C++ Language Specification
Not sure why, but one benefit is that there is no dangling-catch issue. See dangling-else for an ambiguity that can arise when braces are optional.
The syntax of try-block is:
try compound-statement handler-sequence
where handler-sequence is a sequence of one or more handlers, which have the following syntax:
catch (type-specifier-seq declarator) compound-statement
catch (...) compound-statement
This is different from other statements like control statements (if, while, for, etc). The syntax for these are:
if (condition) statement-true else statement-false
while (condition) statement
for (init-statement; condition; iteration_expression) statement
etc.
Now, The question is why compound-statement is needed in the try-block instead of a single statement?
Think about this code:
int main()
{
// before try-statement.
try g(); catch (std::runtime_error e) handleError(e);
// after try-statement.
}
I know, catch by value is a bad practice (e.g. possible object slicing, etc), but I did it in order to prevent a discussion about the storage duration of the exception and make it easy to reason about.
Now think, about the storage duration and linkage of 'e'. What you expect, is that 'e' only can be referred just before the call to handleError function, but no after the call is completed. It should have automatic storage duration and no linkage in this "scope". This could probably done by implicitly define a local scope like in other statements, but make the exception-declaration looks like a function parameter was probably a better idea. So the block (compound-statement) is needed. Se bellow.
Now think about the try and the statement after that. There is no reason to use the keyword try there, and no reason to use a compound statement, but the syntax could become ambiguous and complicated.
This is what Stroustrup said about it in Exception Handling for C++:
It might be possible to simplify the
try { ... } catch (abc) { ... }
syntax by removing the apparently redundant try keyword,
removing the redundant parentheses, and by allowing a handler
to be attached to any statement and not just to a block. For
example, one might allow:
void f()
{
g(); catch (x1) { /* ... */ }
}
as an alternative to - 28 -
void f()
{
try { g(); } catch (x1) { /* ... */ }
}
The added notational convenience seems insignificant and may not
even be convenient. People seem to prefer syntactic constructs that
start with a prefix that alerts them to what is going on, and it may
be easier to generate good code when the try keyword is required.
And after a more detailed explanation:
Allowing exception handlers to be attached to blocks only and not to
simple statements simplifies syntax analysis (both for humans and
computers) where several exceptions are caught and where nested
exception handlers are considered (see Appendix E). For example,
assuming that we allowed handlers to be attached to any statement
we could write:
try try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... }
The could be interpreted be in at least three ways:
try { try f(); catch (x) { ... } } catch (y) { ... } catch (z) { ... }
try { try f(); catch (x) { ... } catch (y) { ... } } catch (z) { ... }
try { try f(); catch (x) { ... } catch (y) { ... } catch (z) { ... } }
There seems to be no reason to allow these ambiguities even if there
is a trivial and systematic way for a parser to chose one
interpretation over another. Consequently, a { is required after a
try and a matching } before the first of the associated sequence of
catch clauses.
As Stroustrup said, without the braces, the statement could mean different things depending on the rule and you will probably need to put braces to clarify the intension. Can we make some that looks complicated with the if-statement like in Stroustrup's example? Of course we can, something like this for example:
if (c1) if (c2) f(); else if (c3) g(); else h();
This is actually equivalent to:
if (c1) { if (c2) f(); else { if (c3) g(); else h(); } }
But I think this is less problematic than the case of try-block. There is two syntax for the if-statament:
if (condition) statement-true
if (condition) statement-true else statement-false
because it make sense not to have a else action sometimes. But it make no sense a try-block without a catch-clause. The 'try' can be omitted but not practical, as Stroustrup said, but the catch-clause can not if you specified a try-block. Beside of this, there could be more than one catch related to the same try-block but only one is executed based in rules that depends on the exception type and order of the catch-clauses.
Now, what if the syntax of if-else is changed to:
if (condition) compound-statement-true else compound-statement-false
then, you must write if-else like this:
if (c1) { f(); } else { if (c2) { g(); } else { h(); } }
See that there is no 'elseif' keyword, no special syntax for 'else if'. I think that even the 'put braces always' defenders don't like to write like this, and write this instead:
if (c1) { f(); } else if (c2) { g(); } else { h(); }
I think that this is not a strong reason to define the syntax as above and introduce in the language a 'elseif' keyword or define a special syntax for 'else if'.
Read this link. Most of the reason appears to be about managing the scope and allocation of objects that need to be created and destroyed in case of real exceptions.
So, my guess is, the grammar writers of C++ are asking the authors of g++(or any standards complying C++ compiler) to prepare it for the worst possible cases, and g++ authors appear to have done so.
Why? A tradeoff between safety and backwards compatibility.
The lessons learnt from if...else showed that requiring braces eliminates errors. Now, the ISO C++ people have a strong preference for backwards compatibility with C, so they didn't change the C syntax for if...else. But new constructs require braces to demarcate controlled blocks, as they won't appear in old C code and therefore backwards compatibility is not a concern.
Well, first, that's how the grammar works.
Second, I would believe that the goal is to forcibly generate a new scope for the exception blocks(correct me if I'm wrong).
That's how they wanted to be. There is no justification, it's a law.
Not sure if you're using .NET but the CLR uses the braces as flags.
http://dotnet.sys-con.com/node/44398
From the article: "The SEH (structure exception handling) table consists of a set of clauses that describe the structure of the guarded code. The table has a set of binary flags that describe the type of exception handling clause: a Try Offset flag, which is the beginning of the guarded code block; a Try Length flag, which is the length of the guarded code; Handler Offset and Handler Length flags, which detail the beginning of the exception handler block and its length; and a Class Token or Filter Offset flag, depending on the type of Exception Handler that was defined. This information allows the CLR to determine what to do when an exception occurs. It maps out the beginning of the guarded code block, the code to execute for an exception, and special semantics related to filtering or other special circumstance."
I would assume that other frameworks do the same thing.
Mainly it's because
if (a)
int b = 10;
else
int b = 5;
b += 5;
Will fail because the if...else without {} is a syntax shortcut for this
if (a) {
int b = 10;
} else {
int b = 5;
}
b += 5;
which explicitly tells you that int b is in a different scope than the rest of the software.
If I'm not mistaken the following also fails
a ? int b = 10 : int b = 5;
b += 5;
Granted, your compiler might optimize that code for you... but it should technically fail because of the scopes in the if/else statement.
Whenever you see {} you're defining the scope of the software.
-Stephen
In C++ you can initialize a variable in an if statement, like so:
if (CThing* pThing = GetThing())
{
}
Why would one consider this bad or good style? What are the benefits and disadvantages?
Personally i like this style because it limits the scope of the pThing variable, so it can never be used accidentally when it is NULL. However, i don't like that you can't do this:
if (CThing* pThing = GetThing() && pThing->IsReallySomeThing())
{
}
If there's a way to make the above work, please post. But if that's just not possible, i'd still like to know why.
Question borrowed from here, similar topic but PHP.
The important thing is that a declaration in C++ is not an expression.
bool a = (CThing* pThing = GetThing()); // not legit!!
You can't do both a declaration and boolean logic in an if statement, C++ language spec specifically allows either an expression or a declaration.
if(A *a = new A)
{
// this is legit and a is scoped here
}
How can we know whether a is defined between one term and another in an expression?
if((A *a = new A) && a->test())
{
// was a really declared before a->test?
}
Bite the bullet and use an internal if. The scope rules are useful and your logic is explicit:
if (CThing* pThing = GetThing())
{
if(pThing->IsReallySomeThing())
{
}
}
About the advantages:
It's always recommended to define variables when you first need them, not a line before. This is for improved readability of your code, since one can tell what CThing is without scrolling and searching where it was defined.
Also reducing scope to a loop/if block, causes the variable to be unreferenced after the execution of the code block, which makes it a candidate for Garbage Collection (if the language supports this feature).
if (CThing* pThing = GetThing())
It is bad style, because inside the if you are not providing a boolean expression. You are providing a CThing*.
CThing* pThing = GetThing();
if (pThing != NULL)
This is good style.
You can have initialization statements inside if and switch since C++17.
Your code would now be:
if (CThing* pThing = GetThing(); pThing->IsReallySomeThing())
{
// use pThing here
}
// pThing is out of scope here
One reason I don't normally do that is because of the common bug from a missed '=' in a conditional test. I use lint with the error/warnings set to catch those. It will then yell about all assignments inside conditionals.
Just an FYI some of the older Microsoft C++ compliers(Visual Studios 6, and .NET 2003 I think) don't quite follow the scoping rule in some instances.
for(int i = 0; i > 20; i++) {
// some code
}
cout << i << endl;
I should be out of scope, but that was/is valid code. I believe it was played off as a feature, but in my opinion it's just non compliance. Not adhering to the standards is bad. Just as a web developer about IE and Firefox.
Can someone with VS check and see if that's still valid?
This shoulddoesn't work in C++ sinceeven though it supports short circuiting evaluation. MaybeDon't try the following:
if ((CThing* pThing = GetThing()) && (pThing->IsReallySomeThing()))
{
}
err.. see Wesley Tarle's answer
So many things. First of all, bare pointers. Please avoid them by all means. Use references, optional, unique_ptr, shared_ptr. As the last resort, write your own class that deals with pointer ownership and nothing else.
Use uniform initialization if you can require C++11 (C++14 preferred to avoid C++11 defects): - it avoids = vs == confusion and it's stricter at checking the arguments if there are any.
if (CThing thing {})
{
}
Make sure to implement operator bool to get predictable conversion from CThing to bool. However, keep in mind that other people reading the code would not see operator bool right away. Explicit method calls are generally more readable and reassuring. If you can require C++17, use initializer syntax.
if (CThing thing {}; thing.is_good())
{
}
If C++17 is not an option, use a declaration above if as others have suggested.
{
CThing thing {};
if (thing.is_good())
{
}
}
You can also enclose the assignment in an extra set of ( ) to prevent the warning message.
I see that as kind of dangerous. The code below is much safer and the enclosing braces will still limit the scope of pThing in the way you want.
I'm assuming GetThing() sometimes returns NULL which is why I put that funny clause in the if() statement. It prevents IsReallySomething() being called on a NULL pointer.
{
CThing *pThing = GetThing();
if(pThing ? pThing->IsReallySomeThing() : false)
{
// Do whatever
}
}
also notice that if you're writing C++ code you want to make the compiler warning about "=" in a conditional statement (that isn't part of a declaration) an error.
It's acceptable and good coding practice. However, people who don't come from a low-level coding background would probably disagree.