I have been programming in C++ for a long time and it keeps surprising me. C++ Programming Language is full of surprises. Lately, I found an interesting one.
I never declare new variables/objects in the case statements. To me, it is not allowed. If I ever need new variables/objects in a case statement, I use braces. Braces define a valid scope for the new variables, the new variables are not valid outside the braces. Without using the braces, variables declared in a case statement are visible in the succeeding cases as well. And these declarations can be skipped if the switch jumps to those cases.
The surprise is that declaration is possible! But only for a very specific case, the declaration without initialization. int var; is such example.
The declaration without initialization is possible only for POD types (plain old data, collection of basic types, C structs based on basic types, pointers, enums etc.). Therefore, to be precise, the declaration in a case statement is possible only for POD types and without initialization.
Declaration without initialization [Allowed, Surprise!]
- int count;
- float length;
- int* ptr;
Declaration with initialization [Not allowed as expected]
- int count = 20;
- float length = 6.7;
- int* ptr = 0;
- std::string str; // involves call to default constructor (initialization)
- std::string str2(“manjeet”);
In the following code. I have marked the statements Valid/Invalid as per g++ and MSVC. Let us look at the unexpected and expected statements.
int var1; // VALID
int var2 = 22; // INVALID
int var2; // VALID
var2 = 22; // VALID
str1 = "test"; // VALID, defined before switch statement
std::string str2("test"); // INVALID
- Line#6: Valid, int var1;
- Line#8: Invalid, int var2 = 22; if Line#6 is valid then this should also be valid.
- Line#11 & 13: Valid, usual assignments.
- Line#14: Invalid, declaration of an object. It is not allowed, very much expected.
As per the C++ Standard ISO/IEC-14882-2003 section 6.7.3
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).
Therefore according to the above rule, jumping past a declaration with initialization is not allowed. And the only exception to this rule could be a declaration of a POD types. Because POD types can be declared without initialization.
int a; // declaration without initialization
And for non-POD types declaration-without-initialition is never possible. The object can’t be declared without initializing because the constructor will always be called.
std::string str; // declaration that includes initialization, constructor is called
Rationale Behind the Rule
The question pops up to the mind.
Why is jumping past a declaration-without-initialization allowed?
I don’t have the answer. But it might have something to do with followings:
- All the initializations of the variables and objects are done at the compile time. Whereas assignments during the runtime of the program.
- If initialization doesn’t takes place the destructor should not be called. But the destruction always takes place when the object goes out of scope. Destruction without construction doesn’t sound good. Therefore the execution should not jump an initialization.
- Declaration of POD types without initialization is allowed in a case statement.
- Declaration of non-Pod types can only be done in the braces only.
- In a switch-case, all the case statements are in the same scope.
- The switch-case is nothing but a collection of goto and labels.
- goto-label jump is not allowed if jump skips declaration with initialization.
- Good practice would be to always use braces after case statements if declarations are involved.
- C++ Standard ISO/IEC-14882-2003