Why does this code compile without complaining about constructors? - c++

A while ago I wrote some code with a set of nested classes. Looking over it today I'm wondering why it compiles when I instantiate an object of type C.
My confusion is this. B has a private constructor. The implementation of this constructor takes care of the construction of A, but the construction of A requires an instance of B. I feel like it's a chicken and the egg scenario. The construction of B requires the construction of A which requires the construction of B etc etc ad infinitum.
I have the following classes - stripped right back to demo the issue:
// ******* A ******** //
class A {
public:
A(A& _Parent, int id);
private:
A& Parent;
};
inline A::A(A& _Parent, int id)
: Parent(_Parent)
{
}
// ******* B ******** //
class B:public A {
public:
static B& GetInstance();
private:
B();
};
inline B::B()
: A(B::GetInstance(), 0)
{
}
inline B& B::GetInstance()
{
static B b;
return b;
}
// ******* C ******** //
class C:public A {
public:
C();
};
inline C::C()
: A(B::GetInstance(), 0)
{
}

Narrowing in on the problem:
inline B& B::GetInstance()
{
static B b;
return b;
}
inline B::B()
: A(B::GetInstance(), 0)
{
}
The line static B b; creates a B object the first time the function is called. However, the construction of b then calls GetInstance, which reaches the line static B b; while b is still under construction.
This case is covered by C++14 [stmt.dcl]/4:
[...] such a variable is considered initialized upon the completion of its initialization. [...] If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
I excised a part talking about what happens if an exception is thrown, or if two differently threads concurrently try to initialize the static variable. The standard does permit local static variables to be initialized prior to the first call of the function, but even if the implementation did that, the same problem would arise of control re-entering the declaration recursively.
Being undefined behaviour, anything can happen. One possible result is it appearing to work as intended. The standard does not require this to be diagnosed at compile-time -- a difficult thing to analyze. As indicated in comments, one version of gcc detects this at runtime and throws an exception. Perhaps your original compiler implements local statics by having a flag to indicate whether execution has reached that line yet, and it sets the flag before calling the constructor.
The rationale for having undefined behaviour in the standard is to avoid placing constraints on the way an implementation implements the well-defined behaviour (in this case, the method of ensuring local statics are only initialized once).
Of course, you should find a way to fix the problem as the undefined behaviour may manifest itself differently in future in ways that are hard to predict.

Although you didn't show any such code, let's assume something attempts to call the default constructor of C or in some other way calls B::GetInstance().
The first statement in GetInstance() is static B b;. Since this is the first time we've arrived here, the object b needs to be initialized by calling the default constructor B::B().
The first thing B::B() does is call GetInstance(), intending to then pass the result of that to the A::A(A&, int) constructor.
Which puts us back again at the statement static B b;. And the C++ Standard says ([stmt.dcl]/4):
Dynamic initialization of a block-scope variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization.... If control enters the declaration recursively while the variable is being initialized, the behavior is undefined.
Undefined behavior means anything could happen. It might appear to work, it might crash or hang your program, or it might initialize incorrectly and continue.

Related

C++ class instance not initialized but no compile error, why

My question is about the following code:
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
class Blob {
public:
int age;
void hello() { printf("hello\n"); }
};
void test_case() {
Blob a;
//a.hello(); //(1)
cout << a.age << endl;
}
int main() {
test_case();
return 0;
}
if I comment out (1), it compile success. If uncomment out (1), compile error occur, e.g. in VS2017 it complains "using an unintialized local variable 'a'".
I've searched in search engine for a while and now only know the following 4 cases that the compiler will automatically help me define the default constructor:
a class member is an instance of a class (say B), and class B have defined default constructor
a class is derived from another class (say B), and B has defined default constructor
a class has virtual function
any combination of the previous 3 cases.
I'm curious that, if I comment out (1), will the compiler add a definition of constructor for the class Blob?
In fact it has nothing to do with comment out or not a.hello();, Blob always has a generated default constructor, otherwise Blob a; won't compile.
(emphasis mine)
If no user-declared constructors of any kind are provided for a class
type (struct, class, or union), the compiler will always declare a
default constructor as an inline public member of its class.
If the implicitly-declared default constructor is not defined as
deleted, it is defined (that is, a function body is generated and
compiled) by the compiler if odr-used, and it has the same effect as a
user-defined constructor with empty body and empty initializer list.
As the result, a.age is default-initialized to indeterminate value, any access to it (like cout << a.age;) leads to UB.
3) when a base class or a non-static data member is not mentioned in a
constructor initializer list and that constructor is called.
otherwise, nothing is done: the objects with automatic storage
duration (and their subobjects) are initialized to indeterminate
values.
It depends on your intent; as the workaround you can add a user-defined default constructor.
class Blob {
public:
int age;
void hello() { printf("hello\n"); }
Blob() : age(42) {}
};
There are 2 points here.
First is the Undefined Behaviour question. As you do not initialize age, it contains an indeterminate value which leads to UB if you use it (see songyuanyao's answers for more details on that). Adding an additional instruction does not change anything to that point.
Next is the compiler message. Compilers are not required to issue any warning facing UB. Yours is not specially consistent if it raises the error in only one case, but the programmer is supposed to never write UB. So you cannot really blame the compiler for not issuing the warning.
TL/DR: do not expect a C++ compiler to alway issue warning when you write incorrect code. Having no warning is a necessary condition, but not a sufficient one.

Why am I permitted to declare an object with a deleted destructor?

Consider the following text:
[C++11: 12.4/11]: Destructors are invoked implicitly
for constructed objects with static storage duration (3.7.1) at program termination (3.6.3),
for constructed objects with thread storage duration (3.7.2) at thread exit,
for constructed objects with automatic storage duration (3.7.3) when the block in which an object is created exits (6.7),
for constructed temporary objects when the lifetime of a temporary object ends (12.2),
for constructed objects allocated by a new-expression (5.3.4), through use of a delete-expression (5.3.5),
in several situations due to the handling of exceptions (15.3).
A program is ill-formed if an object of class type or array thereof is declared and the destructor for the class is not accessible at the point of the declaration. Destructors can also be invoked explicitly.
Then why does this program compile successfully?
#include <iostream>
struct A
{
A(){ };
~A() = delete;
};
A* a = new A;
int main() {}
// g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Is GCC just being permissive?
I'm inclined to say so, since it rejects the following yet the standard appears to have no particular rule specific to deleted destructors in an inheritance hierarchy (the only loosely relevant wording is pertinent to the generation of defaulted default constructors):
#include <iostream>
struct A
{
A() {};
~A() = delete;
};
struct B : A {};
B *b = new B; // error: use of deleted function
int main() {}
The first part is not ill-formed because the standard text doesn't apply - no object of type A is declared there.
For the second part, let's review how object construction works. The standard says (15.2/2) that if any part of construction throws, all fully constructed subobjects up to that point are destroyed in reverse order of construction.
This means that the code underlying a constructor, if all written out by hand, would look something like this:
// Given:
struct C : A, B {
D d;
C() : A(), B(), d() { /* more code */ }
};
// This is the expanded constructor:
C() {
A();
try {
B();
try {
d.D();
try {
/* more code */
} catch(...) { d.~D(); throw; }
} catch(...) { ~B(); throw; }
} catch(...) { ~A(); throw; }
}
For your simpler class, the expanded code for the default constructor (whose definition is required by the new expression) would look like this:
B::B() {
A();
try {
// nothing to do here
} catch(...) {
~A(); // error: ~A() is deleted.
throw;
}
}
Making this work for cases where no exception can possibly be thrown after initialization for some subobject has been completed is just too complex to specify. Therefore, this doesn't actually happen, because the default constructor for B is implicitly defined as deleted in the first place, due to the last bullet point in N3797 12.1/4:
A defaulted default constructor for class X is defined as deleted if:
[...]
any direct or virtual base class or non-static data member has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.
The equivalent language exists for copy/move constructors as the fourth bullet in 12.8/11.
There is also an important paragraph in 12.6.2/10:
In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member of class type is potentially invoked.
It's that B's destructor is generated by the compiler at the line of your error and it has a call to A's destructor which is deleted, hence the error. In the first example nothing is trying to call A's destructor hence no error.
My guess is that this is what happens.
The implicitly generated B() constructor will first of all construct its base class subobject of type A. The language then states that if an exception is thrown during execution of the body of the B() constructor, the A subobject must be destroyed. Hence the need to access the deleted ~A() - it is formally needed for when the constructor throws. Of course, since the generated body of B() is empty this can never ever happen, but the requirement that ~A() should be accessible is still there.
Of course, this is 1) just a guess from my side of why there is an error in the first place and 2) not in any way a quote of the standardese to say whether this would actually be formally ill-formed or just an implementation detail in gcc. Could perhaps give you a clue of where in the standard to look though...
Accessibility is orthogonal to deletedness:
[C++11: 11.2/1]: If a class is declared to be a base class (Clause 10) for another class using the public access specifier, the public members of the base class are accessible as public members of the derived class and protected members of the base class are accessible as protected members of the derived class. If a class is declared to be a base class for another class using the protected access specifier, the public and protected members of the base class are accessible as protected members of the derived class. If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.
There is this:
[C++11: 8.4.3/2]: A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [ Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. If a function is overloaded, it is referenced only if the function is selected by overload resolution. —end note ]
But you never "refer to" the deleted destructor.
(I still can't explain why the inheritance example doesn't compile.)

static object and member method called before constructor

I have two classes A and B such that A has a static B instance as its member. B has a function Show() and here is my class A:
class A
{
A()
{
_b.Show();
}
private:
static B _b;
};
and subsequent code is
A a;
B A::_b;
int main()
{
}
Now B::Show() called before B is constructed due to the sequence in which I have defined
a and _b. But how does this work exactly i.e. how is it possible to make calls on an object that is still not constructed ?
It's not possible, it's undefined behavior (in this case, because you're accessing an uninitialized object) because a is initialized before A::_b.
Look up static initialization order fiasco. You don't get an error because 99% of the times this happens, it's not easily diagnosable.
This is the static initialisation order problem. Hard to solve in the general case, but one way to fix some cases is to put the dependency inside a function like this:
class A
{
public:
A()
{
getB().Show();
}
private:
static B& getB()
{
static B b;
return b;
}
};
statics within functions are guaranteed to be initialised the first time that a call is made to it. Additionally in C++11 this is guaranteed to be thread safe as well.
Further reading:
http://www.parashift.com/c++-faq/static-init-order-on-first-use.html
Finding C++ static initialization order problems
how is it possible to make calls on an object that is still not
constructed?
Well, the simple answer is, exactly the way that code does it. <g>. Objects defined at file scope in the same translation unit are constructed in the order in which their definitions occur, so if the constructor of one object relies on another object, you have to ensure that the second object has already been constructed when the constructor for the first object runs.
C++ doesn't offer any help here; you have to keep track of it yourself.

Why are member variables visible in c++ constructor initialization lists?

I came across this due to a bug in my code and I'm curious why it's allowed. What reason is there that allows object members to be visible in the constructor initialization list?
#include <stdio.h>
class derived {
private:
int * value2;
public:
derived();
};
derived::derived()
: value2(value2){} // Uninitialized self-assignment
int main()
{
derived thisChild;
}
Clang gives a warning about this but unfortunately g++ does not.
So you can initalise one member using another; this is perfectly fine if the other has already been initialised, or if you're just using its address to initialise a pointer or reference. For example:
struct Thingy
{
int & r;
int a;
int b;
Thingy(int x) :
r(a), // OK - a has a valid address, and we're not using the value
a(x),
b(a) // OK - a has been initialised
{}
};
It would be rather tricky to allow that and disallow your example.
If it is not visible, you cannot write this:
A(int n) : some_num(n), another_num(some_num*10) {}
then what would be the point of member-initialization list?
As for self-initialization (uninitialized variable), you can do even this:
int xyz = xyz; //will compile
You can think of the initialization list as part of the body of the method (specifically the constructor), so it is natural that you can access the member variables of an object in one of its methods
Also, you might want to reuse an already created member variable to initialize others -- note: you will have to know the exact order of initialization (order of member variable declaration) to make sure you are using this portably
I think you mean why are member variables accessible in initializer expressions.
One reason is that the initialization of one member variable can depend on another.
That's fragile, but sometimes necessary to avoid awkward code such as artificial base classes.

Cyclic Constructors

Suppose structs A and B are singleton structs defined as follows:
struct A
{
B& b_;
static A& shared_a() { ... }
A() : b_(B::shared_b()) { ... }
};
struct B
{
A& a_;
static B& shared_b() { ... }
B() : a_(A::shared_a()) { ... }
};
Suppose that the file structure is organized so that the code will compile.
The first time A::shared_a is called, it will construct the shared instance of A. The constructor for the shared instance of A will call B::shared_b, which will construct the shared instance of B. Next, the constructor for the shared instance of B will call A::shared_a. However, the shared instance of A has not finished its constructor! Therefore, these constructors will loop infinitely.
In order to prevent such a loop, I could merge classes A and B, but I would like to avoid doing so. Is there a more elegant solution?
Thanks,
Sam
Your code exhibits undefined behavior, and you might get either the infinite loop that you mention or any other weird behavior. Now, the problem is not with how to solve it, but rather break the cyclic dependency, which is usually a code smell.
If you are still convinced that your design makes sense, and if your constructors only store the references (without using the objects) you can change the constructors to take a reference to the object.
Again, I would avoid the cyclic dependency.
How about you give the shared_a() a reference of B itself so in the A_constructor of shared_a it can set that reference to be its b_?