I'm wondering, for no other purpose than pure curiosity (because no one SHOULD EVER write code like this!) about how the behavior of RAII meshes with the use of goto (lovely idea isn't it).
class Two
{
public:
~Two()
{
printf("2,");
}
};
class Ghost
{
public:
~Ghost()
{
printf(" BOO! ");
}
};
void foo()
{
{
Two t;
printf("1,");
goto JUMP;
}
Ghost g;
JUMP:
printf("3");
}
int main()
{
foo();
}
When running the following code in Visual Studio 2005 I get the following output.
1,2,3 BOO!
However I imagined, guessed, hoped that 'BOO!' wouldn't actually appear as the Ghost should have never been instantiated (IMHO, because I don't know the actual expected behavior of this code).
What's up?
I just realized that if I instantiate an explicit constructor for Ghost the code doesn't compile...
class Ghost
{
public:
Ghost()
{
printf(" HAHAHA! ");
}
~Ghost()
{
printf(" BOO! ");
}
};
Ah, the mystery ...
The standard talks about this explicitly - with an example; 6.7/3 "Declaration statement" (emphasis added by me):
Variables with automatic storage duration are initialized each time their declaration-statement is executed. Variables with automatic storage duration declared in the block are destroyed on exit from the block.
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 and is declared without an initializer.
[Example:
void f()
{
//...
goto lx; //ill-formed: jump into scope of a
//...
ly:
X a = 1;
//...
lx:
goto ly; //OK, jump implies destructor
//call for a, followed by construction
//again immediately following label ly
}
—end example]
So it seems to me that MSVC's behavior is not standards compliant - Ghost is not a POD type, so the compiler should issue an error when the the goto statement is coded to jump past it.
A couple other compilers I tried (GCC and Digital Mars) issue errors. Comeau issues a warning (but in fairness, my build script for Comeau has it configured for high MSVC compatibility, so it might be following Microsoft's lead intentionally).
Goto isn't radioactive. Leaving by goto is little different from leaving by exception. Entering by goto should be dictated by convenience, not the limits of the language. Not knowing whether the ghost is constructed or not is a good reason not to do that.
Jump in before the constructor. If you want to jump in after some object is already constructed, enclose it in a new scope or otherwise resolve its lifetime yourself.
In this scenario, I have found following approach useful.
void foo()
{
{
Two t;
printf("1,");
goto JUMP;
}
{
Ghost g;
// operations that use g.
}
// g is out of scope, so following JUMP is allowed.
JUMP:
printf("3");
}
Confining the scope of variable g in your foo() function, will make the goto jump legal. Now, we are not jumping from a place where g is not initialized to a place where g is expected to be initialized.
Related
void someFunc()
{
int local0 = 0;
//some code
{
int local1 = 0;
//some code
}
int local2 = 0;
}
All three local variables will be created(allocated on the stack) at the moment of entering someFunc? Or local0 will be created first, then local1, then local1 will be deleted, and then local2 created and on the exit local0 and local2 will be deleted?
So, this question is hard to answer in the abstract. The compiler is free to reorder your code, so long as it is equivalent to the code as written if executed sequentially. That is to say, what assembly is generated is up to the compiler.
With this specific code, and this specific code alone, a compiler performing any optimization is highly likely to realize this function is a no-op, and elide it (godbolt).
That said, as general guidance, C++ does not perform mandatory variable hoisting (as with JS, see here). That is, it is at best undefined behavior (at worst, errant syntax) to use the names of variables before they are declared.
Edit: As Deduplicator mentioned in the comments, the as-if rule formalizes how compilers can transform code. It specifies that code changes which do not affect the "observable behavior" of the program are allowed. I like John Regehr's definition of observable behavior (here), though it is a little tautological:
Observable behaviors are those that are side effecting.
C++ is defined in terms of an abstract machine. In the abstract machine the operations happen in this order:
local0 is created
local1 is created
local1 is destroyed
local2 is created
The requirements on the real machine are only that it must produce the same output that the abstract machine would . There are no requirements about how it goes about producing that output.
For this particular program the real machine might not create any of the variables since they are unused and removing them doesn't affect the output.
It's easier to get a handle on this if you use variables whose construction and destruction have side-effects. Then you can see them coming and going.
So, given this:
class Foo
{
int m_i;
public:
Foo (int i) : m_i (i) { std::cout << "Foo constructor #" << i << "\n"; }
~Foo () { std::cout << "Foo destructor #" << m_i << "\n"; }
};
We can then rewrite your example as:
void someFunc()
{
Foo local0 (0);
{
Foo local1 (1);
}
Foo local2 (2);
}
Which gives the output:
Foo constructor #0
Foo constructor #1
Foo destructor #1
Foo constructor #2
Foo destructor #2
Foo destructor #0
Which in turn makes everything clear.
As others have mentioned, if the variables are primitive types (and don't depend on each other) then the compiler is free to reorder the statements in order to generate faster code.
class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();
class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}
IA* GetA()
{
static A a;
return &a;
}
int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}
Obviously, class A's constructor function calls itself.
"static A a" will get stuck in a loop.
On VS2013, this code can get out from the loop and print "End" on the console.
On VS2017, this code gets stuck in a loop.
**What does VS2013 do for this code?
Nothing in particular has to happen. According to the C++ standard:
[stmt.dcl] (emphasis mine)
4 Dynamic initialization of a block-scope variable with static storage
duration or thread storage duration is performed the first time
control passes through its declaration; such a variable is considered
initialized upon the completion of its initialization. If the
initialization exits by throwing an exception, the initialization is
not complete, so it will be tried again the next time control enters
the declaration. If control enters the declaration concurrently while
the variable is being initialized, the concurrent execution shall wait
for completion of the initialization. If control re-enters the
declaration recursively while the variable is being initialized, the
behavior is undefined. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
— end example ]
The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).
Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.
But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.
The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.
Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.
Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.
goto or switch can jump over a declaration-statement given that it has no initializer and the construction is trivial — and that the object is also trivially destructible.
What's the rationale for the constraint on the destructor?
struct trivial {
trivial() = default;
~ trivial() = default;
};
struct semi_trivial {
semi_trivial() = default;
~ semi_trivial() noexcept { do_something(); }
};
void foo() {
goto good_label; // OK
trivial foo;
good_label:
goto bad_label; // Error: this goto statement
semi_trivial bar; // cannot jump over this declaration.
bad_label:
std::cout << "hi\n";
}
The current wording is a result of N2762. The paper gives the following rationale:
6.7 stmt.dcl:
Jumping over the definition of an automatic variable will pose the problem of whether the destructor for that variable should be run at the end of the block. Thus, the destructor needs to be trivial, i.e. have no effect. Similarly, the default constructor (the one potentially used to initialize the object) is also required to not do anything, i.e. be trivial. No other requirements are necessary.
I think the case to keep in mind is:
int i = 2;
switch (i) {
case 1:
semi_trivial st;
do_something(st);
break;
case 2:
break; // should st be destructed here?
}
And indeed, this is not an easy question to answer. Calling the destructor there would not be the obviously right thing to do. There's no good way of telling whether it should be called. The st variable here is only used in the case 1 statements, and programmers would be surprised if its destructor got called by the case 2's break statement even though it was completely unused there and not constructed.
I decided to do a test with computed gotos and local statics
void g() { std::cout << "init "; }
void f() {
int z = 0;
y: z++;
static int x =
(g(), z == 1 ? ({ goto *&&y; 0; }) : 0);
}
int main() { f(); std::cout << "!"; f(); }
I wanted to see whether the output would be "init init !". But to my surprise I didn't get that output, but instead GCC handled it gracefully, outputting at runtime:
init terminated by recursive_init_error: exception
What's that exception? Is it a Standard exception? C++03 or C++0x? Thanks for any explanation.
It's caused by what is stated in C++03 §6.7/4:
... Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined. [Example:
int foo(int i)
{
static int s = foo(2*i); // recursive call – undefined
return i+1;
}
--end example]
GCC throws an exception in that case. Here's some documentation about it.
C++11 update: The following wording was added in C++11, just before the text about the recursive case:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.88
88 The implementation must not introduce any deadlock around execution of the initializer.
Doesn't change the problem here, but does make this construct thread-safe when there is no recursion.
In my project I found a piece of code in which a method was getting called in constructor's initializer list.
Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
I observed that there is a chance that the users of Test2 might pass NULL to the constructor. Since the pointer is used without validation there are chances of Access Violation.
This triggered me to look into exception handling in constructor's initializers list. I found in one of the article that try can be used inside initializer list. I wrote small test program to test this concept:
//Test class stores the unique ID and returns the same with API getTestID
class Test
{
public:
Test(int nID):m_nID(nID){
}
int getTestID() const
{
return m_nID;
}
private:
int m_nID;
};
class Test2
{
public:
Test2(Test* pTest)
try :m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
catch (...)
{
cout<<"exception cought "<< endl;
}
void printDupID()
{
cout<<"Duplicate ID" << m_nDuplicateID << endl;
}
private:
Test* m_pTest;
int m_nDuplicateID;
};
int main(int argc, char* argv[])
{
Test* pTest = new Test(10);
Test2 aTest2(pTest);
aTest2.printDupID();
delete pTest;
return 0;
}
This code is not getting compiled in VC6.0. Do I need to make any changes to make it compile in VC 6.0?
Also, in one of the article I found that using try in constructor's initializer list does not strictly confirms to C++ standards. In that case, how do we handle the exceptions in constructor's initializers list (standard way of handling )?
Thanks.
C++ Standard Section 15 / 3
A function-try-block associates a
handler-seqwith thector-initializer,
if present, and the function-body. An
exception thrown during the execution
of the initializer expressions in the
ctor-initializer or during the
execution of the function-body
transfers control to a handler in a
function-try-block in the same way as
an exception thrown during the
execution of a try-block transfers
control to other handlers.
class C
{
int i;
double d;
public:
C(int, double);
};
C::C(int ii, double id)
try : i(f(ii)), d(id)
{
//constructor function body
} catch (...)
{
//handles exceptions thrown from the ctor-initializer
//and from the constructor functionbody
}
Firstly, if you dereference the NULL pointer standard C++ does not guarantee that that an exception will be thrown, so your code is useless for this case.
Secondly, if an exception were thrown, what would your exception handler do?
Thirdly, constructor/function exception blocks are widely considered to be awaste of time - take a look at this http://www.gotw.ca/gotw/066.htm and other articles on Herb Sutter's GotW site.
According to this article, it looks like you just can't do that in VC++ 6.0
You'd either have to upgade to 7.0 or just do the initialization in the constructor body instead.
Can't you just use a function to check the ptr, e.g.:
template<typename P>
P* checkPtr (P* p)
{
if (p == 0)
throw std::runtime_error ("Null pointer");
return p;
}
class Test2
{
public:
Test2 (Test* pTest)
: m_pTest (checkPtr (pTest))
{
}
Test* m_pTest;
};
People still use VC6?
Seriously, VC6 is hardly a standards-complaint compiler. Do yourself a favor and at least get VS2005. VC6 is your problem.
Try VS2008 express and see if it compiles.
The other option, of course, is to take a reference on construction, which needs to be bound.
(for fellow googlers)
Another solution if we don't want to store a copy of the ptr / shared_ptr :
class Foo::Pimpl
{
public:
bool paramTest_;
Pimpl(ConstNodePtr root)
try :
paramTest_( root ? true : throw std::invalid_argument("Foo (pimpl) constructed from NULL node")),
...
{
...
} catch (...)
{
throw; // rethrow
}
There are many useful answers already, but I'll try to add a little bit, maybe it will help someone.
First of all, as others already mentioned - dereferencing a nullptr or invalid pointer (address) doesn't throw an exception in the standard C++. MSVC supports it through its Structured Exception Handling, but it's not portable. Read more about it in this answer.
The function try block in a constructor doesn't allow you to suppress the exception, it will propagate anyway if you don't throw another one. And when you enter the catch clause, all members of the class are already destroyed. So the only plausible thing you can do in it is to log the error somehow or maybe change some global variables. That's why it's considered more or less useless.
As for your initial code
Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
{
}
you could use the ternary operator to check for pTest's nullness just in the initializer list and do some appropriate actions in case it is null - just set m_nDuplicateID to nullptr or some other value depending on its type, call another function and use it return type, etc:
Test2(Test* pTest):
m_pTest(pTest),
m_nDuplicateID( pTest ? pTest->getTestID() : /*some value or call*/ )
{
}
you could even use several nested ternary operators to create more complicated execution flows.
And just for completeness, it's not the case with your code, but it may worn someone in the same situation. If you used your class's member m_pTest to initialize m_nDuplicateID, that would depend on the order of these members in class's declaration, because class members in the initializer list get initialized in the order of declaration and not in the order they appear in the initializer list itself, so it can be a problem and it's better to avoid members initialization order dependencies:
class A
{
A( B* pTest );
int m_nDuplicateID;
B* m_pTest;
};
A::A( B* pTest ) :
m_pTest( pTest ),
m_nDuplicateID( m_pTest->someMethod() ) // here m_pTest isn't initialized yet,
// so access violation probably
{
}