I came across the following rule (3.7.1/2 N3797):
If a variable with static storage duration has initialization or a
destructor with side effects, it shall not be eliminated even if it
appears to be unused, except that a class object or its copy/move may
be eliminated as specified in 12.8.
Is it true that, if we declare a variable with static storage duration, having constructor or destructor with side-effect, then the memory will allocate for that variable even if it is unused?
In particular, I'm trying to experiment with the following code:
#include <csignal>
#include <iostream>
#include <cstdlib>
using std::cout;
struct data_member
{
data_member(){ cout << "data_member\n"; }
~data_member(){ cout << "~data_member\n"; }
};
struct Y
{
static data_member m;
Y(){ cout << "Y\n"; }
~Y(){ cout << "~Y\n"; }
};
Y y;
int main()
{
}
IdeOne
Why does data_member object not constructed in the example?
Well, that's what the standard says, so ... yes.
As always, there's the as-if rule, allowing a compiler to do practically anything, as long as the net effect is the same. I could imagine that if the class is huge data-wise, but the constructor and destructor (and anybody else) do not access that data, the compiler could just call the constructor and destructor in the proper order, without leaving space for the unused data.
As for your example, you're declaring Y::m, but you never define it. So it doesn't exist. If you tried to access it, you'd get a linker error.
Related
I've previously worked in a setting where exceptions have been turned off and failed memory allocation means that we kill the program. Now working with exceptions I'm wondering about the precise semantics of the following:
class Foo {
std::unique_ptr<Bar> x;
std::unique_ptr<Bar> y;
public:
Foo(): x{new Bar}, y{new Bar} {}
};
My question is what happens when new Bar throws when y is being allocated? I would assume that the destructor of x is called so that the first allocation is cleaned up. How is the language guaranteeing this? Anyone know a quote from the standard that explains the precise semantics?
Yes, all completely-constructed members will be destroyed. Your object will not be left in any sort of "half-alive" state. No memory leaks will occur.
[except.ctor]/3: If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed ([dcl.init]) [..] The subobjects are destroyed in the reverse order of the completion of their construction. [..]
We can demonstrate this ourselves:
#include <memory>
#include <iostream>
struct Bar
{
Bar(const char* name, bool doThrow = false) : m_name(name)
{
if (doThrow)
{
std::cout << name << ": Bar() throwing\n";
throw 0;
}
std::cout << name << ": Bar()\n";
}
~Bar() { std::cout << m_name << ": ~Bar()\n"; }
private:
const char* m_name;
};
class Foo {
std::unique_ptr<Bar> x;
std::unique_ptr<Bar> y;
public:
Foo(): x{new Bar("A")}, y{new Bar("B", true)} {}
};
int main()
{
try
{
Foo f;
}
catch (...) {}
}
// g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
// A: Bar()
// B: Bar() throwing
// A: ~Bar()
(live demo)
This is, in fact, one of the major benefits of so-called "smart pointers": exception safety. Were x a raw pointer, you'd have leaked the thing it pointed to, because a raw pointer's destruction doesn't do anything. With exception safety you can have RAII; without it, good luck.
If you are worried about the two new Bar expressions interleaving and throwing before the handles are initialized to hold what they are meant, the standard doesn't allow it.
First in [intro.execution]
12 A full-expression is
an init-declarator or a mem-initializer, including the constituent expressions of the initializer,
16 Every value computation and side effect associated with a
full-expression is sequenced before every value computation and side
effect associated with the next full-expression to be evaluated.
Without going too much into details, x{new Bar} and y{new Bar} in their entirety are both considered what standardese deems a "full-expression" (even though they are not expressions grammar-wise). The two paragraphs I quoted indicate that either the entire initialization of x (which includes new Bar) has to happen first, or the entire initialization of y has to happen first. We know from [class.base.init]
13.3 - Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order
of the mem-initializers).
So x is initialized in full, and then y. So even if new Bar throws while initializing y, x already owns the resource it's meant to hold. In which case, when the exception is thrown, the verbiage in [except.ctor] parageph 3 will apply to the fully constructed x, and it will be destructed, thus releasing the resource.
If an exception is thrown during object construction the object was not constructed and you shouldn't make any assumptions about the state of any members. The object is simply not usable. Any members that were initialized prior to the exception being thrown will be destructed in reverse order of their construction.
https://eel.is/c++draft/basic.life#def:lifetime describes lifetime rules.
Regarding the book "Effective C++" from Scot Meyers, and the 4th item: non-local static objects can be uninitialized before the are used (static in this case means "global", with static life). If you replace it with a local-static object, which is created inside a function that returns a reference to it, the object is then for sure initialized before use.
I always have a file with constants. I declare extern const int a; in an .hpp file and define it in the .cpp file. But can then the same thing happen? a can be uninitialized. Or not? Does the same rule apply for built-in types?
Even though you can, it's not such a good idea to return references to "local-static" variables. The variable was (presumably) declared locally to reduce its scope to just the enclosing function, so attempting to increase its scope in this manner is rather hacky. You could make it a global variable and use something like std::call_once to guarantee it's initialized exactly once on first usage. Returning a mutable reference to a local-static object also raises thread-safety concerns because the function may no longer be re-entrant.
POD types with static storage duration are guaranteed to be zero-initialized. You can also initialize them with a constant expression and the language will guarantee they are initialized before any dynamic initialization takes place. Here's a similar question that may provide some additional insight.
The problem regarding static initialization is known as static initialization order fiasco:
In short, suppose you have two static objects x and y which exist in
separate source files, say x.cpp and y.cpp. Suppose further that the
initialization for the y object (typically the y object’s constructor)
calls some method on the x object.
So if you have another translation unit using your constants, you have a rather good chance that your program will not work. Sometimes it is the order the files were linked together, some plattforms even define it in the doc (I think Solaris is one example here).
The problem also applies to builtin types such as int. The example from the FAQ is:
#include <iostream>
int f(); // forward declaration
int g(); // forward declaration
int x = f();
int y = g();
int f()
{
std::cout << "using 'y' (which is " << y << ")\n";
return 3*y + 7;
}
int g()
{
std::cout << "initializing 'y'\n";
return 5;
}
int main() {
std::cout << x << std::endl << y << std::endl;
return 0;
}
If you run this example, the output is:
using 'y' (which is 0)
initializing 'y'
So y first gets zero-initialized and then constant initialization (?) happens.
The solution is the Construct On First Use Idiom:
The basic idea of the Construct On First Use Idiom is to wrap your
static object inside a function.
Static loca objects are constructed the first time the control flow reaches their declaration.
cin, cout, basic streams related - is it guaranteed anywhere in the standard that these obejcts will be created first and destroyed last?
It would implicate that non-local static objects can rely on them in their constructors and destructors (no ctor race between these objects and the basic streams).
They are guaranteed to be created before any static object declared after including <iostream> and, in any case, before starting main. They are not destroyed during program execution.
Including the header has the effect of declaring a static variable of type ios_base::Init, whose creation ensures that the standard streams are initialised.
If you want the Standardese for this:
C++11 27.4.1 [iostream.objects.overview]/2: The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution. The objects are not destroyed during program execution. The results of including <iostream> in a translation unit shall be as if <iostream> defined an instance of ios_base::Init with static storage duration. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.
The simple answer to your question is no. As others have pointed out,
there are guarantees for objects defined in translation units
including <iostream>, at least if the object is defined after the
inclusion. But this doesn't always help: you include <iostream> in
the translation unit which defines the constructor, not necessarily in
the one which defines the static variable. So cases like the following
are possible:
file1.hh
class X
{
public:
X();
};
file1.cc
#include "file1.hh"
#include <iostream>
X::X()
{
std::cout << "Coucou, me voila!" << std::endl;
}
file2.cc
#include "file1.hh"
X anX;
In this case, it's quite possible that the constructor of anX be
called before std::cout is constructed.
To be on the safe side: if the constructor of an object which might be
used as a static variable wants to use any of the standard streams, it
should probably declare a local static of type ios_base::Init:
X::X()
{
static ios_base::Init dummyForInitialization;
std::cout << "Coucou, me voila!" << std::endl;
}
If std::cout wasn't already constructed when this constructor is
called, it will be when the static variable is constructed.
Going by the document here http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
“Variables with static storage duration (3.7.1) or thread storage
duration (3.7.2) shall be zero-initialized (8.5) before any other
initialization takes place”
If I have everything, i.e class declaration and main() in a single file (a must) I should be able to omit the initialization.
But, if I omit, I get "undefined reference" error during build.
#include <iostream>
using namespace std;
class foo
{
public:
static int array[2];
};
int foo::array[2] = {0}; //I should be able to omit this line
int main()
{
cout << "foo::array[0] = " << foo::array[0] << endl;
return 0;
}
PS: No C++11
I think you are misreading the standard. You can simply drop the = {0} part as the compiler will automatically initialize it with zeros.
You can not leave out the entire line because otherwise you just declare the array but you never define it anywhere - that's what is causing the problem for you.
For a static data member of a class, one must compulsorily provide definition in the implementation file because,
static data has a single piece of storage regardless of how many objects are created, that storage must be defined in a single place. The compiler will not allocate storage for you. The linker will report an error if a static data member is declared but not defined.
As you alreay know, the order of constructions of variables that are both defined outside of a function and different compilation units is not fixed. With this understanding, i devised simple codes that i think have to crash while running. However, It just didn't. Here are the codes.
/* table.h */
#include <iostream>
using namespace std;
class table {
public:
int a;
int b;
table(table& t) {
a = t.a;
b = t.b;
}
void print(void) {
cout << a << endl;
cout << b << endl;
}
};
/* f1.cpp */
#include "./table.h"
extern table t2;
table t1 = t2;
/* f2.cpp */
#include "./table.h"
extern table t1;
table t2 = t1;
/* core.cpp */
#include "./table.h"
extern table t1;
extern table t2;
int main(void) {
t1.print();
t2.print();
return 0;
}
As you can see, the t1 and t2 are referring each other. Although we're not sure which one will be allocated first, it is evident that one is to access the other that are not allocated, which is a total disaster. It's so weird it works fine.
It doesn't have to crash - it is just undefined behavior. You never initialize the member variables with real values, so the output will be garbage no matter what order they are initialized.
As they are global variables, their memory space is reserved/allocated at the very beginning of the execution. The problem is the order in which the constructors are called. If you reference the object before it has been constructed, the member variables will have whatever value happened to be there already.
The initialization of global objects is not ordered across translation units.
The typical way to ensure that an object is initialized before use is to wrap it into a function like this:
myfoo.h:
#include "Foo.h"
Foo & myFoo();
myfoo.cc:
#include "myfoo.h"
Foo & myFoo()
{
static Foo impl;
return impl;
}
Everyone who needs the global object includes myfoo.h and refers to the global object as myFoo().
If you try this approach on your problem (suitably modified to account for the initializers), you will see that your problem is ill-defined, as you will enter the same static initialization more than once.
i think have to crash while running
No; it has (borderline) undefined behaviour, but there's no requirement for undefined behaviour to cause a crash. If there were, then the behaviour would be defined.
it is evident that one is to access the other that are not allocated
They have been allocated, just not (dynamically) initialised. The storage for static objects is allocated, and zero-initialised, in the static initialisation phase before any user code is run. Then, during dynamic initialisation, each object is initialised using the zero-valued memory of the other.
It's so weird it works fine
Formally, accessing the objects' values before initialisation gives undefined behaviour. In practise, you will simply access the zero-valued memory with no indication of the error.