Please note that this is a question about C++ language, not about how real or useful is the example I'm giving to illustrate it.
Imagine we have an enum in a namespace (or namespaces):
namespace SomeVeryLargeNamespaceExample {
enum class E {
One,
Two,
};
}
Now, we want to use it in the expression of a switch statement. Today I found that it is possible to add the using SomeVeryLargeNamespaceExample::E inside the switch statement, before the first case, reducing the code cluttering in the cases:
switch (e) {
using SomeVeryLargeNamespaceExample::E;
case E::One:
std::cout << "One\n";
break;
case E::Two:
std::cout << "Two\n";
break;
}
At first glance I thought it was some kind of "do this before any case statement" feature that I never learned about, but some expressions, such as a function call, are not executed (actually, gcc generates a warning statement will never be executed [-Wswitch-unreachable]). On the other hand, a variable declaration is possible (but not initialization).
My question is, what other statements are possible? Is this a feature or just a consequence of how switch is designed?
Note: I found this other question, but it is specific for C (so it doesn't mention the namespaces case, for example), and I'm curious about C++.
Any valid C++ statement is possible:
int a;
void foo(int x)
{
switch (x) {
a=4;
case 0:
a=1;
break;
case 1:
a=2;
break;
}
}
This is syntactically valid C++, and gcc has no issues compiling it and producing an executable. However the initial statement can never be reached, of course. Therefore every modern C++ compiler will give you a warning message:
t.C: In function ‘void foo(int)’:
t.C:8:18: warning: statement will
never be executed [-Wswitch-unreachable]
8 | a=4;
|
Related
This question already has answers here:
Defining a variable in the condition part of an if-statement?
(5 answers)
Closed 1 year ago.
Consider this code:
if (int* p = nullptr) {
}
else {
std::cout << *p << std::endl;
}
This compiles with clang 7.0.1 with -std=c++17, -Wall, -Wextra and -pedantic-errors without a single warning generated.
I've got two questions:
How is this possible? I always thought that scope of such variables ends with the if-block. I'd be thankful for quotation from the standard if possible.
If it's legal though, shouldn't it at least generate a warning? Are there any reasonable applications of such feature?
Why is variable declared in an if-statement still in scope in else block?
Because the standard says that it is. It was designed so presumably because it is useful.
How is this possible?
It's unclear why it wouldn't be possible.
I always thought that scope of such variables ends with the if-block.
You assumed wrongly.
I'd be thankful for quotation from the standard if possible. [language-lawyer]
Latest draft says:
[stmt.select.general]
Selection statements choose one of several flows of control.
selection-statement:
...
if constexpropt ( init-statementopt condition ) statement else statement
...
Note that the entire if (condition) statement else statement is a selection-statement.
[basic.scope.block]
Each
selection or iteration statement ([stmt.select], [stmt.iter]),
...
introduces a block scope that includes that statement or handler.
Note that the condition is directly within the if-statement and not inside a sub-statement, and thus a declaration within it extends until the end of the entire block scope, which contains both the if-sub-statement, and the else-sub-statement (not standard names for those sub-statements).
There is also a pretty clear example that demonstrates ill-formed re-declarations, and incidentally shows the scope of such declaration:
if (int x = f()) {
int x; // error: redeclaration of x
}
else {
int x; // error: redeclaration of x
}
If it's legal though, shouldn't it at least generate a warning?
Yeah, it would be nice if compilers were able to detect all provable null pointer indirections at compile time. It may be worth submitting a feature request regarding this corner case.
Are there any reasonable applications of such feature?
Sure. Following fabricated example seems reasonable to me:
if (Response r = do_request()) {
log << "great success. content: " << r.content;
} else {
log << "someone broke it :( error: " << r.error_code;
}
Or if you dislike implicit conversions:
if (Response r = do_request(); r.is_success()) {
This question already has answers here:
Changing switch variable inside a case
(4 answers)
Closed 9 years ago.
Will it work/is it allowed to have a switch-case statement and change the key in a statement?
For example:
temp = 1
switch(temp)
{
case 1:
temp = 2;
//Do something with temp..
break;
}
The point of doing this is just that I don't have to create another variable, and it also makes sense to do so in my program. I am not asking about changing to another case in the switch-case statement, just if it's allowed to reuse the variable used as the key without any side effects.
This is perfectly fine. Virtually every state machine on the planet does something equivalent to what you do here.
Yes, you may do that. In fact, the following is common practice for writing lexers:
int c = getc(file);
switch(c)
{
case '+':
c = getc(file); // < `c` modified here!
switch(c)
{
case '+': return OP_INC;
default: ungetc(c, file); return OP_ADD;
}
...
}
Though it is not a good idea to use the same variable however the code will work because the branch is determined in the moment when the expression in the switch statement is evaluated.
I have found this very useful post and I`d like to clarify something about the compiler optimizations. Lets say we have this function (same like in the original post):
template<int action>
__global__ void kernel()
{
switch(action) {
case 1:
// First code
break;
case 2:
// Second code
break;
}
}
Would the compiler do the optimization in the sense of eliminating an unreachable code even in the case I called the function with template variable unknown in the time of compiling - something like creating two separete functions? E.g.:
kernel<argv[1][0]>();
Short answer: no.
Templates are instantiated and generated purely at compiletime, so you can't use the values in argv, since they are not known at compile time.
Makes me wonder why you did not just give it a try and threw that code at a compiler - it would have told you that template arguments must be compile time constants.
Update:
Since you told us in the comments that it's not primarily about performance, but about readability, i'd recommend using switch/case:
template <char c> void kernel() {
//...
switch(c) { /* ... */ }
}
switch (argv[1][0]) {
case 'a':
kernel<'a'>();
break;
case 'b':
kernel<'b'>();
break;
//...
}
Since the value you have to make the descision on (i.e. argv[1][0]), is only known at runtime, you have to use runtime descision mechanisms. Of those, switch/case is among the fastest, especially if there are not too many different cases (but more than two) and especially if there are no gaps between the cases (i.e. 'a', 'b', 'c', instead of 1, 55, 2048). The compiler then can produce very fast jumptables.
Being new to templates I`d had to study some essential matters. Finally I came up with the solution to my problem. If I want to call functions with template parameters depending on command line arguments I should do it like this:
if(argv[1][0] == '1')
kernel<1><<< ... >>>();
if(argv[1][0] == '2')
kernel<2><<< ... >>>();
I also checked ptx file of such program and found out that compiler makes in this case optimization producing two different kernel functions without switch statement.
My professor commonly asks my class how many statements there are in a given program, but I can't determine what he defines as a statement. It seems as though an if/else is one statement, and a for loop is one statement regardless of if there are other supposed statements within it. Are there any governing rules for this matter or is his definition of his own invention?
Thanks
For a precise definition of a statement:
Definition: A statement is a block of code that does something. An assignment statement assigns a value to a variable. A for statement performs a loop.
In C, C++ and C# Statements can be grouped together as one statement using curly brackets
{
statement1;
statement2;
}
As far as counting statements, I agree with the others, there's not much point. Counting Lines of Code (LOC) though, actually has some value and there's a lot of research that tries to relate the number of LOC to the workload of developers. It's possible that your instructor is having you count statements and thinking of statements as nothing more than a single LOC, which isn't quite the case.
Statements nest, i.e. smaller statements can be joined into larger statements, like compound statements. For this reason, the question about "how many statements are there in this program" are ambiguous. One has to define the counting method first. Without it the question of "how many" makes little sense.
Here is the function that handles statements parsing in JS alike language:
static void do_statement(CsCompiler *c )
{
int tkn;
switch (tkn = CsToken(c)) {
case T_IF: do_if(c); break;
case T_WHILE: do_while(c); break;
case T_WITH: do_with(c); break;
case T_DO: do_dowhile(c); break;
case T_FOR: do_for(c); break;
case T_BREAK: do_break(c); CsSaveToken(c,CsToken(c)); break;
case T_CONTINUE: do_continue(c); CsSaveToken(c,CsToken(c)); break;
case T_SWITCH: do_switch(c); break;
case T_CASE: /*do_case(c);*/ CsParseError(c,"'case' outside of switch"); break;
case T_DEFAULT: /*do_default(c);*/ CsParseError(c,"'default' outside of switch"); break;
case T_RETURN: do_return(c); break;
case T_DELETE: do_delete(c); break;
case T_TRY: do_try(c); break;
case T_THROW: do_throw(c); break;
case '{': do_block(c, 0); break;
case ';': ; break;
default:
{
CsSaveToken(c,tkn);
do_expr(c);
break;
}
}
}
As you see it includes things like for, while and also expressions (separated by ;)
In computer programming a statement
can be thought of as the smallest
standalone element of an imperative
programming language. A program is
formed by a sequence of one or more
statements. A statement will have
internal components (e.g.,
expressions).
More at Statement (Computer Science) at Wikipedia.
Example:
switch( x )
{
case y:
if ( true )
{
break;
}
cout << "Oops";
break;
}
If the switch statement selects y, will Oops be written to the standard output?
- Is break in switch statements a dynamic keyword like continue which can be called under conditions or static like a closing bracket }?
break breaks out of an enclosing switch, while, for, or do ... while. It doesn't break out of an enclosing if or bare block. Pretty much exactly like continue.
It's certainly not part of the required syntax of a switch statement (like a close-brace is part of the syntax of blocks). break is a statement, essentially meaning "transfer execution to the point after the end of the innermost breakable construct".
No, Oops will not written out, the break jumps behind this statement.
You can use break statements conditionally just fine, you only have to watch out when you create nested statements that also support break (i.e. loops).
break is absolutely dynamic. So, if you write if (false) break; your Oops would be written out.
Think of break in this case as a goto end_of_switch.
No, it will not be printed in your case. break only breaks from switches and loops, not conditionals or functions or arbitrary enclosing scopes. Therefore, a more relevant question would be whether Oops is printed in this case:
switch( x )
{
case y:
for ( int i = 0; i < 10; ++i )
{
break;
}
cout << "Oops";
break;
}
And the answer here is yes, it will. The break will break out of the for, not the switch.
No. It would break out of the switch construct. "break" is used to come out of the innermost loop construct or switch construct. So, in your case the "oops" is not printed out on the screen. If you want that to happen, in your code, you could use if(false) instead of if(true).