variable declaration inside switch case label

S

subramanian100in

Consider the following program:

#include <iostream>

using namespace std;

int main()
{
int i;

cin >> i;

switch ( i )
{
case 1:
int case_1 = 1;
cout << "case_1 " << case_1 << endl;
break;
case 2:
int case_2 = 2;
cout << "case_2 " << case_2 << endl;
break;
}

return 0;
}

If a variable is declared inside any case label except the last case
label, I am getting compilation error. In this program I am getting
compilation error for the declaration

int case_1 = 1;

inside case 1.

But there is no compilation error for the declaration

int case_2 = 2;

inside case 2.

Kindly explain what is the difference and why is the declaration not
allowed in other case labels but allowed in the last case label in a
switch ?
 
R

rsprawls

If a variable is declared inside any case label except the last case
label, I am getting compilation error. In this program I am getting
compilation error for the declaration

int case_1 = 1;

inside case 1.

But there is no compilation error for the declaration

int case_2 = 2;

inside case 2.

Kindly explain what is the difference and why is the declaration not
allowed in other case labels but allowed in the last case label in a
switch ?

It's been a long, long time since I programmed and I'm starting to
pick it back up, so bear with me if I'm wrong:

The fact that erroring out on the first case statement and not on the
second leads me to believe it's not reporting the error on case_2
because it's not processing anymore after case_1, because it's already
in error. Have you tried removing the int case_1 = 1 to see if it
errs on the case_2 = 2 line?
 
J

John Harrison

Consider the following program:

#include <iostream>

using namespace std;

int main()
{
int i;

cin >> i;

switch ( i )
{
case 1:
int case_1 = 1;
cout << "case_1 " << case_1 << endl;
break;
case 2:
int case_2 = 2;
cout << "case_2 " << case_2 << endl;
break;
}

return 0;
}

If a variable is declared inside any case label except the last case
label, I am getting compilation error. In this program I am getting
compilation error for the declaration

int case_1 = 1;

inside case 1.

But there is no compilation error for the declaration

int case_2 = 2;

inside case 2.

Kindly explain what is the difference and why is the declaration not
allowed in other case labels but allowed in the last case label in a
switch ?

Because it's a rule of C++ that a jump cannot pass over a variable
declaration in the same scope. So when you jump to case 2, you pass over
the variable declaration in case 1. But the variable declaration in the
last case is OK, because it is never jumped over.

The reason for the rule is that if you allowed a jump over a variable
declaration it would be very hard for the compiler to work out whether
to call a destructor for that variable. If you had jumped over the
variable declaration you would not need to call the destructor, if you
had not jumped then you would.

If you want to decalre variable inside switch statements, do it like this

switch ( i )
{
case 1:
{
int case_1 = 1;
cout << "case_1 " << case_1 << endl;
}
break;
case 2:
{
int case_2 = 2;
cout << "case_2 " << case_2 << endl;
}
break;
}

The extra { and } mean that the compiler has no trouble working out when
to call destructors.

Of there are no ddestructors for int variables, but there could be for
other variable types, and it was decided to made all variables the same
for this rule.

john
 
B

Bharath

Because it's a rule of C++ that a jump cannot pass over a variable
declaration in the same scope. So when you jump to case 2, you pass over
the variable declaration in case 1. But the variable declaration in the
last case is OK, because it is never jumped over.

The reason for the rule is that if you allowed a jump over a variable
declaration it would be very hard for the compiler to work out whether
to call a destructor for that variable. If you had jumped over the
variable declaration you would not need to call the destructor, if you
had not jumped then you would.

If you want to decalre variable inside switch statements, do it like this

switch ( i )
{
case 1:
{
int case_1 = 1;
cout << "case_1 " << case_1 << endl;
}
break;
case 2:
{
int case_2 = 2;
cout << "case_2 " << case_2 << endl;
}
break;
}

The extra { and } mean that the compiler has no trouble working out when
to call destructors.

Of there are no ddestructors for int variables, but there could be for
other variable types, and it was decided to made all variables the same
for this rule.

john

John - Thanks for good information. Is this documented somewhere?

Bharath
 
J

John Harrison

John - Thanks for good information. Is this documented somewhere?

Bharath

Well it's documented in the C++ standard, section 6.7.3, but I guess any
good C++ book would cover this.

Looking at the standard though, there's an exception to this rule which
I wasn't aware of. If the variable is a POD type (int is a POD type for
instance) and if the declaration does not have an initialiser then you
are allowed to 'jump' the variable declartion. I.e. if you had written

switch ( i )
{
case 1:
int case_1;
case_1 = 1;
cout << "case_1 " << case_1 << endl;
break;
case 2:
int case_2;
case_2 = 2;
cout << "case_2 " << case_2 << endl;
break;
}

the code would been legal (assuming I'm reading this right). Not sure
what the rationale is for that.

john
 
P

Pete Becker

John said:
Well it's documented in the C++ standard, section 6.7.3, but I guess any
good C++ book would cover this.

Looking at the standard though, there's an exception to this rule which
I wasn't aware of. If the variable is a POD type (int is a POD type for
instance) and if the declaration does not have an initialiser then you
are allowed to 'jump' the variable declartion. I.e. if you had written

switch ( i )
{
case 1:
int case_1;
case_1 = 1;
cout << "case_1 " << case_1 << endl;
break;
case 2:
int case_2;
case_2 = 2;
cout << "case_2 " << case_2 << endl;
break;
}

the code would been legal (assuming I'm reading this right). Not sure
what the rationale is for that.

The issue is skipping the initialization of a variable. If there's no
initialization, then there's nothing to skip, so the jump is harmless.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

John Harrison

Pete said:
The issue is skipping the initialization of a variable. If there's no
initialization, then there's nothing to skip, so the jump is harmless.

I don't understand. For a POD type there doesn't seem to be any
difference between

T x = y;

and

T x;
x = y;

so why is there this situation where on is legal and the other isn't. If
the initialisation (in the first case) is harmful, why isn't the
assignment in the second case? Why ban the first case when the same
effect can be achieved by the second case?

john
 
P

Pete Becker

John said:
I don't understand. For a POD type there doesn't seem to be any
difference between

T x = y;

and

T x;
x = y;

so why is there this situation where on is legal and the other isn't. If
the initialisation (in the first case) is harmful, why isn't the
assignment in the second case? Why ban the first case when the same
effect can be achieved by the second case?

How would you succinctly describe the second case in order to write a
rule prohibiting skipping it? The rule is simply that skipping
initializers isn't allowed.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

John Harrison

Pete said:
How would you succinctly describe the second case in order to write a
rule prohibiting skipping it? The rule is simply that skipping
initializers isn't allowed.

Well granted that would be difficult. But why not allow both cases? That
what I'm suggesting. In other words non-POD declarations cannot be
skipped, but POD ones can with or without initialiser. What would be the
harm?

john
 
J

James Kanze

Well it's documented in the C++ standard, section 6.7.3, but I guess any
good C++ book would cover this.
Looking at the standard though, there's an exception to this rule which
I wasn't aware of. If the variable is a POD type (int is a POD type for
instance) and if the declaration does not have an initialiser then you
are allowed to 'jump' the variable declartion. I.e. if you had written
switch ( i )
{
case 1:
int case_1;
case_1 = 1;
cout << "case_1 " << case_1 << endl;
break;
case 2:
int case_2;
case_2 = 2;
cout << "case_2 " << case_2 << endl;
break;
}
the code would been legal (assuming I'm reading this right). Not sure
what the rationale is for that.

The problem is that if i == 2, and you jump to case 2, case_1 is
in scope. If case_1 had an initializer (either explicit
initialization, or a non-trivial constructor), presumable any
use of it without initialization is an error. if it doesn't,
presumably, you've covered your bets otherwise.

I think someone mentionned destructors. In fact, it has nothing
to do with destructors; the destructor of both case_1 and case_2
will be called when they go out of scope, at the closing brace
of the switch. Again, presumably, if the object didn't have a
non-trivial constructor, and wasn't initialized, presumably,
calling the destructor on it is OK.

You gave the key to understanding the rules in your first
answer, indirectly. A switch (and the break in each of the
cases) has the semantics of a goto. The real rule is that you
are not allowed to jump over a non-trivial initialization (but
leaving scope by any means causes the destructors to be called).
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Well granted that would be difficult. But why not allow both cases? That
what I'm suggesting. In other words non-POD declarations cannot be
skipped, but POD ones can with or without initialiser. What would be the
harm?

It's quite late, so I might miss something here but are you suggesting
that it should be allowed to skip initialization of POD-types? Consider
the following (while I hope to god that a pointer is a POD*):

int* p;
goto skip;
p = new int;
skip:
*p = 1;

I just can't see any way to make it safe to skip initialization without
introducing a lot of special cases.

* And should a pointer not be a POD-type we can just replace it with
struct POD { int* p}
 
J

John Harrison

Erik said:
It's quite late, so I might miss something here but are you suggesting
that it should be allowed to skip initialization of POD-types? Consider
the following (while I hope to god that a pointer is a POD*):

int* p;
goto skip;
p = new int;
skip:
*p = 1;

I just can't see any way to make it safe to skip initialization without
introducing a lot of special cases.

* And should a pointer not be a POD-type we can just replace it with
struct POD { int* p}

I don't understand, your code compiles. Will crash at run time of
course, but that's not the point.

I'm talking about the fact that it is a compile error to skip an
initialisation.

// illegal
goto skip;
int* p = new int;
skip:
*p = 1;

// legal
goto skip;
int* p;
p = new int;
skip:
*p = 1;

This is very strange, and I still don't understand.

john
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

I don't understand, your code compiles. Will crash at run time of
course, but that's not the point.

Oh, right, it was a bit too late to post.
I'm talking about the fact that it is a compile error to skip an
initialisation.

// illegal
goto skip;
int* p = new int;
skip:
*p = 1;

// legal
goto skip;
int* p;
p = new int;
skip:
*p = 1;

This is very strange, and I still don't understand.

The rule is that it's not allowed to skip a declaration of a type unless
the type is POD and not initialized. The reason you can't skip a
declaration of a non-POD is that it will always initialize the variable
(by running the constructor).

The following is pure speculation on my part but I would guess that the
reason you can't skip a declaration of a POD-type with initialization is
because that would violate the intent of the programmer. If I write
something like

int i = 1;

it means that I want there to be a variable named i with the value of 1,
and I can depend on it being 1 until I change it. The reason you are
allowed to skip a declaration (without initialization) is probably
because it's easy to implement and allows you to do some things you
wouldn't otherwise (though you can always rewrite to get the same effect).
 
P

Pete Becker

John said:
I don't understand, your code compiles. Will crash at run time of
course, but that's not the point.

That IS the point. When an object is explicitly initialized, it's
because that initialization is important. If you haven't bothered to
initialize it, it's because you don't care whether it's initialized, and
subsequent code is responsible for handling that uninitialized object
correctly.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

James Kanze

[...]
The rule is that it's not allowed to skip a declaration of a type unless
the type is POD and not initialized. The reason you can't skip a
declaration of a non-POD is that it will always initialize the variable
(by running the constructor).

Not quite. The rule is that it is not allowed to skip a
definition of a type if the definition has an explicit
initializer or if the type has a non-trivial constructor.
According to the standard, something like the following is
legal:

struct S
{
~S() { std::cout << "coucou, me voila" << std::endl ; }
} ;

goto skip ;
S anS ;
skip:
; // ...

(In practice, of course, it doesn't make a difference, because
if we provide a user defined destructor, there's a reason, and
that reason will almost always render the construct
non-trivial.)
The following is pure speculation on my part but I would guess that the
reason you can't skip a declaration of a POD-type with initialization is
because that would violate the intent of the programmer. If I write
something like
int i = 1;
it means that I want there to be a variable named i with the value of 1,
and I can depend on it being 1 until I change it. The reason you are
allowed to skip a declaration (without initialization) is probably
because it's easy to implement and allows you to do some things you
wouldn't otherwise (though you can always rewrite to get the same effect).

More or less, yes. I rather suspect that the idea was
originally to apply it to classes which had a user-defined
constructor; it is guaranteed that no instance of such a class
shall exist without the constructor having been run on it. But
the logic necessary to verify this easily extends to the more
general case of any initialization; doing more, on the other
hand, requires a lot more work.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top