In C primer 5th edition the solutions appendix, gives an example about switch
statement:
case true:
string file_name; // error: control bypasses an implicitly initialized variable.
...
case false:
if(file_name.empty()); // file_name is in scope but wasn't initialized.
But I think it is wrong because file_name
has been implicitly initialized and that is why the compiler flags that error.
struct Foo{};
switch(val){
case 1:
Foo f; // ok not-implicitly initialized like std::string
break;
case 2:
f.some_member(); // use f;
}
So are my guesses correct?
CodePudding user response:
The term "implicit initialization" sounds technical. But it's gobbledygook. It means nothing.
And the compiler doesn't use this made-up term either. To wit:
int main()
{
struct Foo {
void some_member() {}
int a = 1;
};
int val = 0;
switch (val){
case 1:
Foo f;
break;
case 2:
f.some_member(); // use f;
}
}
Compiler output:
<source>: In function 'int main()':
<source>:12:14: error: jump to case label
12 | case 2:
| ^
<source>:10:17: note: crosses initialization of 'main()::Foo f'
10 | Foo f;
| ^
And that's all. Initialization, plain and simple. When you remove the initializer for the member Foo::a
, there's no more initialization, and the compiler will be OK with that:
int main()
{
struct Foo {
void some_member() {}
int a;
};
int val = 0;
switch (val){
case 1:
Foo f; // OK - no initialization at all
break;
case 2:
f.some_member(); // use f
if (f.a) {}; // undefined behavior, a is uninitialized here
}
}
Now, f.a
is not initialized, and that's OK unless you attempt to use its value. The access to f.a
doesn't cause a compile-time failure, but is undefined behavior, just as-if you wrote the following:
int main() {
int a;
if (a) {} // undefined behavior, a isn't initialized
}
Modern compilers can and will use the uninitialized variable/member access as an optimization hint. The code that uses the uninitialized value may be removed, for example.
CodePudding user response:
According for example to the C 17 Standard (9.7 Declaration statement)
3 It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps91 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer
The class std::string
does not has a trivial default constructor and a trivial destructor.
In this code snippet
struct Foo{};
switch(val){
case 1:
Foo f; // ok not-implicitly initialized like std::string
break;
case 2:
f.some_member(); // use f;
}
the class Foo has a trivial default constructor and a trivial destructor.
and for example (15.1 Constructors)
6 A default constructor is trivial if it is not user-provided and if:
(6.1) — its class has no virtual functions (13.3) and no virtual base classes (13.1), and
(6.2) — no non-static data member of its class has a default member initializer (12.2), and
(6.3) — all the direct base classes of its class have trivial default constructors, and
(6.4) — for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.