Suppose I have the following snipplet:
Foo foo;
....
return bar();
Now, does the C++ standard guarantees me that bar() will be called before foo::~Foo() ? Or is this the compiler/implementation's choice?
Thanks!
It is guaranteed behaviour. The actual execution is unrolled as follows:
0: enter block (scope)
1: Foo::Foo()
2. evaluation of bar(); as expression in return statement
3. save result of the expression as value returned from function
4. finalize return statement to leave function to its caller (request exit from current scope)
5: exit block (scope) with call to Foo::~Foo()
Here are some references from the standard:
What program execution guarantees, generally
1.9 Program execution
10 An instance of each object with automatic storage duration (3.7.2) is
associated with each entry into its
block.
The foo is of automatic storage duration and:
3.7.2 Automatic storage duration
1 Local objects explicitly declared auto or register or not explicitly declared
static or extern have automatic storage duration. The storage for
these objects lasts until the block in which they are created exits.
What is actual effect of return statement
6.6.3 The return statement
2 (...) the value of the expression is returned to the caller of the function
and
6.6 Jump statements (return belongs to jump statements)
2 On exit from a scope (however accomplished), destructors (12.4) are called for all
constructed objects with automatic storage duration (3.7.2)
What guarantees that the effect occurs
6.7 Declaration statement
2 Variables with automatic storage duration declared in the block are
destroyed on exit from the block
and
12.4 Destructors
10 Destructors are invoked implicitly (1) for a constructed
object with static storage duration
(3.7.1) at program termination
(3.6.3), (2) for a constructed object
with automatic storage duration
(3.7.2) when the block in which the
object is created exits (6.7)
It is not easy to grasp single idea form details scattered around all the C++ standard. Hopefully, quick overview will help you to make such analysis yourself too.
Yes, bar() will be called before the destructor of foo.
The standard says:
6.6: "On exit from a scope (however accomplished), destructors (12.4) are
called for all constructed objects with automatic storage duration
(3.7.2) (named objects or temporaries) that are declared in that scope,
in the reverse order of their declaration."
The scope isn't left until the return statement is completed.
The result of calling bar() must be evaluated before the stack frame containing Foo can be cleaned up, so yes, bar() will be called before Foo::~Foo().
Objects destruct when leaving the scope.
return leaves the scope, but it can't return until it has executed bar(). Ergo, bar() is called.
Just think, what if it was return bar(foo);? That just has to work, and it'd be silly if the destruction order was different depending on whether you pass that as an argument or not.
Related
The book Object oriented programming in c++ by Robert Lafore says,
A static local variable has the visibility of an automatic local
variable (that is, inside the function containing it). However, its
lifetime is the same as that of a global variable, except that it
doesn’t come into existence until the first call to the function
containing it. Thereafter it remains in existence for the life of the
program
What does coming into existence after first call of function mean? The storage for static local is allocated at the time program is loaded in the memory.
The storage is allocated before main is entered, but (for example) if the static object has a ctor with side effects, those side effects might be delayed until just before the first time the function is called.
Note, however, that this is not necessarily the case. Constant initialization is only required to happen before that block is entered (not necessarily just as execution "crosses" that definition). Likewise, implementations are allowed to initialize other block-scope static variables earlier than required under some circumstances (if you want to get into the gory details of the circumstances, you can look at [basic.start.init] and [stmt.dcl], but it basically comes down to: as long as it doesn't affect the value with which it's initialized. For example, if you had something like:
int i;
std::cin >> i;
{
static int x = i;
...the implementation wouldn't be able to initialize x until the block was entered, because the value with which it was being initialized wouldn't be known until them. On the other hand, if you had:
{
static int i = 0;
...the implementation could carry out the initialization as early as it wished (and most would/will basically carry out such an initialization at compile time, so it won't involve executing any instructions at run-time at all). Even for less trivial cases, however, earlier initialization is allowed when logically possible (e.g., the value isn't coming from previous execution).
In C++ storage duration of an object (when raw memory gets allocated for it) and lifetime of an object are two separate concepts. The author was apparently referring to the latter one when he was talking about object's "coming into existence".
In general case it is not enough to allocate storage for an object to make it "come into existence". Lifetime of an object with non-trivial initialization begins once its initialization is complete. For example, an object of a class with a non-trivial constructor does not officially "live" until its constructor has completed execution.
Initialization of a static local object is performed when the control passes over the declaration for the very first time. Before that the object does not officially exist, even if the memory for it is already allocated.
Note that the author is not painstakingly precise in his description. It is not sufficient to just call the function containing the declaration. The control has to pass through the declaration of the object for it to begin its lifetime. If the function contains branching, this does not necessarily happen during the very first call to the function.
For object with trivial initialization (like int objects), there's no difference between storage duration and lifetime. For such objects allocating memory is all that needs to be done. But in general case allocating memory alone is not sufficient.
It means that the static variable inside a function doesn't get initialized (by the constructor or the assignment operator) until the first call for that function.
As soon as the function, which contains a static local variable, is called the static local variable is initialized.
Lets say foo is called once in a program, and
void foo()
{
if(sometimes_false())
{
static int xx = func_with_sideeffect();
}
}
the condition wasn't met, is the side-effect
1. Allowed to have happened
2. Mandated to have happened (I'm guessing not if my compiler is conforming)
3. Mandated to not have happened
The non-constant initialization of all variable with local scope and static storage duration is from the point it is encountered till the end of the program. So, if the variable is not encountered because of a condition, it would not be initialized and the side effect won't happen.
The following quote from the standard supports the answer (particularly the part in bold)
6.7 Declaration statement [stmt.dcl]
The zero-initialization (8.5) of all block-scope variables with static
storage duration (3.7.1) or thread storage duration (3.7.2) is
performed before any other initialization takes place. Constant
initialization (3.6.2) of ablock-scope entity with static storage
duration, if applicable, is performed before its block is first
entered.An implementation is permitted to perform early initialization
of other block-scope variables with static orthread storage duration
under the same conditions that an implementation is permitted to
statically initializea variable with static or thread storage duration
in namespace scope (3.6.2). Otherwise such a variable is initialized
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.88 If control
re-enters the declaration recursively while the variable is
being initialized, the behavior is undefined.
The answer is 3 but you need to think about threading.
The static int xx will be initialised by the first thread encountering that variable, and C++11 will block all other threads at that point until func_with_sideeffect() returns and the result has been assigned to xx. (That's not the case with older standards: a mutex would have been required).
It's more difficult to predict the destruction of xx if it was an instance of an object with a non-trivial destructor.
I have recently read Andrei Alexandrescu's Modern C++ Design. After reading 6. chapter, I begin to worry about our singletons at company. Since our experienced team leader writes core helper libraries like singletons etc... . I asked him if the way he handles singleton takes care of on dead reference problem ? If he used at_exit function call which is given by C core language?
He told me C++11 has singleton support and will execute CTORs and DTORs in a row that they will not be any dead reference problem. User will not have to cope with synchronization.
Even it sounds awesome I couldn't find any information which confirms him on internet. So please tell me if C++11 takes care of Dead Reference Problem for singletons and if so please explain a little what dark magic going behind ?
Presumably your team leader is talking about singletons implemented as follows:
T &get_value() {
static T val;
return val;
}
In this case, the standard gives two guarantees. The first is that the val objects will be constructed exactly once, the first time that the flow of program execution passes the declaration of the local static variable, even if that happens simultaneously on several threads 6.7/4:
An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized 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.
Static initialisation is only allowed in the case of constants, so as long as T does not have a constexpr constructor you shouldn't have to worry (but read 3.6.2 for the full rules, in case there is some edge case that is relevant to your code).
The second guarantee is that all variables with static storage duration will be destructed in the reverse order of their construction 3.6.3/1:
Destructors (12.4) for initialized objects (that is, objects whose lifetime (3.8) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit (18.5). Destructors for initialized objects with thread storage duration within a given thread are called as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for all initialized objects with thread storage duration within that thread are sequenced before the initiation of the destructors of any object with static storage duration. If the completion of the constructor or dynamic initialization of an object with thread storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. [Note: This definition permits concurrent destruction. — end note ] If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any block-scope object with static storage duration initialized during the construction of the subobjects is destroyed. If the destruction of an object with static or thread storage duration exits via an exception, std::terminate is called (15.5.1).
While this paragraph gives a lot of scope for concurrent destruction of static objects when their construction was concurrent, the main thing to take away from it is that destruction happens in the reverse order of construction.
Together, these mean means that if T val depends on some U val in another of these singleton functions, the U val will always be constructed before T val and destructed after the T val, so overall, this is a safe way of implementing singletons (unless you're doing something very crazy).
Given non-POD type T:
auto p = new T();
::new (p) T();
/* ... */
delete p;
This is UB, right?
Clearly I'm not directly leaking the memory allocated for that first T (and if it has no indirect members then I'm not leaking anything at all), but it never got destructed, which seems to me to be a great candidate for spontaneous annihilation of galaxies populated by sentient cat-like beings.
Thanks to #Xeo for, um, "inspiring" this question in the C++ Lounge.
It rather depends.
[C++11: 3.8/1]: The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
Clearly, this is a case of re-use.
And:
[C++11: 3.8/4]: A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
So, even for a non-POD type T, it's valid iff nothing in your program actually relied on what the destructor was doing.
It's a bit airy-fairy, but it does potentially allow what you're doing.
Note that this leniency does not extend to some only slightly more bizarre cases:
[C++11: 3.8/9]: Creating a new object at the storage location that a const object with static, thread, or automatic storage duration occupies or, at the storage location that such a const object used to occupy before its lifetime ended results in undefined behavior
From my example program, it looks like it does call the destructors in both the cases. At what point does it call the destructors for global and class-static variables since they should be allocated in the data section of the program stack?
From § 3.6.3 of the C++03 standard:
Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling exit (18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any local object with static storage duration initialized during the construction of the sub- objects is destroyed.
Furthermore, § 9.4.2 7 states:
Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).
However, if a destructor has no observable behavior, it may not be invoked. Terry Mahaffey details this in his answer to "Is a C++ destructor guaranteed not to be called until the end of the block?" .
Somewhere after "main"
(you can't know or rely on the exact order in which they are called)