Circular dependency in constructor initialization list - c++

Is the following well-defined?
class A;
class B;
// define A, which takes B& in constructor
// define B, which takes A& in constructor
class C
{
A a;
B b;
public:
C() : a(b), b(a) { /* stuff with a and b */ }
}
Full example at ideone.com.
Is it safe/well-defined so long as the constructors for A and B don't do anything with the references they get?

N4140 [class.cdtor]/1 reads:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object
before the constructor begins execution results in undefined behavior. For an object with a non-trivial
destructor, referring to any non-static member or base class of the object after the destructor finishes
execution results in undefined behavior.
While this passage itself doesn't imply that the behavior is otherwise well-defined, the following example shows that it is. Here is an excerpt:
struct B : public A { int j; Y y; }; // non-trivial
extern B bobj;
B* pb = &bobj; // OK
So the answer is: yes, the behavior in your case is well defined if you aren't referring to members or base classes of b in the constructor of A.

Related

Can the compiler generates a default copy constructor that takes reference to different class type?

I have this example
struct B { B(); };
struct D : B { };
D d{ B() }; // what happens here? or why it's well-formed
This is an aggregate initialization, but I can't understand how d is constructed? Does the compiler generates implicitly a copy constructor with this signature D::D(const D&) or D::D(const B&) or what? It's clear that the compiler does not generate D::D(const D&) because const D& = B() is ill-formed. So this means it generates a copy constructor D::D(const B&)?
Now what would happen if I inherits constructors from B:
struct B { B(); };
struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?
One said to me that the default copy constructor of B which is B::B(const B&) is inherited into D but it's excluded from the set of candidates, that's why it's ill-formed. Is that true?
Can the compiler generates a default copy constructor that takes reference to different class type?
By definition, no. A constructor that accepts an object of another type is not a copy constructor. It would be a converting constructor.
No such converting constructor is implicitly generated.
This is an aggregate initialization, but I can't understand how d is constructed?
No constructor of the enclosing class is called in aggregate initialisation. The sub objects are initialised directly.
D is an aggregate with a base class of type B. Aggregate initialisation is used to initialise this base sub object.
It's clear that the compiler does not generate D::D(const D&) because const D& = B() is ill-formed.
Former cannot be deduced from the latter. In fact, there is a (trivial) D::D(const D&) which you can prove by attempting copy initialisation:
D d1{};
D d2(d1); // works
That said, a trivial constructor is a concept for the abstract machine, and the compiler doesn't have to generate anything in practice.
Now what would happen if I inherits constructors from B
struct D : B { using B::B; };
D d{ B() }; // why it's ill-formed?
Having inherited constructors disqualifies the class from being an aggregate and hence aggregate initialisation does not apply. List initialisation will attempt to call a constructor, but no converting constructor exists.

How to call the constructor of reference members in the member initializer list?

Consider this example:
class C {};
class B {
public:
B(C& c) : c_(c) {};
private:
C& c_;
};
class A {
public:
A(C& c) : b(c) {};
private:
B& b;
};
A has a reference member b of class B. B has a constructor that takes a reference of class C. A's constructor takes a reference of class C and tries to initialize b by calling the latter's constructor with c.
But clang complains with the following message:
wtf.cpp:12:13: error: non-const lvalue reference to type 'B' cannot bind to a value of unrelated type 'C'
A(C& c) : b(c) {};
^ ~
1 error generated.
It sounds almost as if clang thought I was assigning c to b, but my intent is to call B's constructor with c. What am I doing wrong here?
What you describe is not restricted to initializer lists, but to the usual construction of references. The following should not compile:
class C
{};
class B
{
public:
B(C& c)
: c_(c)
{}
private:
C& c_;
};
int main()
{
C c;
B b0(c); // This works because you actually create an object of type B.
B& b1(c); // Error, cannot construct reference to B from C.
B& b2 = c; // Same as above, only a different notation.
// You cannot write a constructor of B to make these lines work,
// because you do not create an object of type B.
}
An object of class B can be constructed from a reference to a C object, but the same does not hold for references. A reference can only be created from an object of the same type, or a type below in inheritance hierarchy.
That is exactly the point of a reference: You do not construct an object. You just introduce a new name for an object that was created somewhere else.
You cannot do that. A reference is in fact close to non modifiable pointer. That means that a B& can only reference a object of class B or of a sub-class of B. And there is nothing like the construction of a reference.
You could construct a temporary B object from the C reference, but you cannot initialize a reference with a temporary object, because as soon as the reference will have be initialized, the referenced object would be destroyed.
So you must store a real object an not a ref in A:
class A {
public:
A(C& c) : b(c) {};
private:
B b;
};

Method as an argument of base class constructor in subclass constructor [duplicate]

OK, member variables can be used to initialize other member variables in an initialization list (with care taken about the initialization order etc). What about member functions? To be specific, is this snippet legal according to the C++ standard?
struct foo{
foo(const size_t N) : N_(N), arr_(fill_arr(N)) {
//arr_ = fill_arr(N); // or should I fall back to this one?
}
std::vector<double> fill_arr(const size_t N){
std::vector<double> arr(N);
// fill in the vector somehow
return arr;
}
size_t N_;
std::vector<double> arr_;
// other stuff
};
Yes, your use of member function in initialization list is valid and complies with the standard.
Data members are initialized in the order of their declaration (and that's the reason why they should appear in the initialization list in the order of their declaration - the rule that you followed in your example). N_ is initialized first and you could have passed this data member to fill_arr. fill_arr is called before constructor but because this function does not access uninitialized data members (it does not access data members at all) its call is considered safe.
Here are some relevant excepts from the latest draft (N3242=11-0012) of the C++ standard:
§ 12.6.2.13: Member functions (including virtual member functions,
10.3) can be called for an object under construction.(...) However, if these operations are performed in a ctor-initializer (or in a function
called directly or indirectly from a ctor-initializer) before all the
mem-initializers for base classes have completed, the result of the
operation is undefined. Example:
class A { public: A(int); };
class B : public A {
int j;
public:
int f();
B() : A(f()), // undefined: calls member function
// but base A not yet initialized
j(f()) { } // well-defined: bases are all initialized
};
class C {
public:
C(int);
};
class D : public B, C {
int i;
public:
D() : C(f()), // undefined: calls member function
// but base C not yet initialized
i(f()) { } // well-defined: bases are all initialized
};
§12.7.1: For an object with a non-trivial constructor, referring to
any non-static member or base class of the object before the
constructor begins execution results in undefined behavior. Example
struct W { int j; };
struct X : public virtual W { };
struct Y {
int *p;
X x;
Y() : p(&x.j) { // undefined, x is not yet constructed
}
};
While initializing objects in the initialization list, the object is not yet fully constructed.
If those function tries to access the part of the object which is not yet constructed then that is a undefined behavior else its fine.
see this answer.

Is this undefined behavior or a false positive warning?

Consider the following code:
class A {
private:
int a;
public:
A(int a) : a(a) { }
};
class B : public A {
private:
int b;
bool init() {
b = 0;
return true;
}
public:
// init() is a hack to initialize b before A()
// B() : b(0), A(b) {} yields -Wreorder
// B() : A((b = 0)) {} no warning (but this one doesn't work so well with non-pod (pointer) types)
B() : A(init() ? b : 0) {}
};
Now trying to compile this code with clang...
$ clang++ test.cpp -fsyntax-only
test.cpp:19:20: warning: field 'b' is uninitialized when used here [-Wuninitialized]
B() : A(init() ? b : 0) {}
^
1 warning generated.
GCC does not print any warnings, not even with -Wall -Wextra and -pedantic.
It's undefined behavior. According to [class.base.init]:
In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes ...
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
— 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).
b won't have been initialized by the time that the A base class is initialized. The assignment b = 0 is itself undefined behavior for the same reason - b hadn't been initialized yet when that is called. Its default constructor would still be called after A's constructor.
If you want ensure that b is initialized first, the typical approach is the base-from-member idiom:
struct B_member {
int b;
B_member() : b(0) { }
};
class B : public B_member, public A
{
public:
B() : A(b) // B_member gets initialized first, which initializes b
// then A gets initialized using 'b'. No UB here.
{ };
};
In either case, calling a member function before base classes are initialized invokes undefined behavior. §12.6.2/16:
Member functions (including virtual member functions, 10.3) can be
called for an object under construction. Similarly, an object under
construction can be the operand of the typeid operator (5.2.8) or of a
dynamic_cast (5.2.7). However, if these operations are performed in a
ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for
base classes have completed, the result of the operation is
undefined. [ Example:
class A {
public:
A(int);
};
class B : public A {
int j;
public:
int f();
B() : A(f()), // undefined: calls member function
// but base A not yet initialized
j(f()) { } // well-defined: bases are all initialized
};
However, the access of and assignment to b itself is fine, since it has vacuous initialization and its lifetime starts as soon as storage is acquired for it (which happened long before the constructor call started). Hence
class B : public A {
private:
int b;
public:
B() : A(b=0) {}
};
is well-defined.

Declaration vs. Instantiation

Comming from Java, I have difficulty with the code below.
In my understanding b is just declared on line 3 but not instantiated.
What would be the text book way of creating an instance of B in class A?
class A {
private:
B b;
public:
A() {
//instantiate b here?
}
};
Edit: What if B does not have a default constructor?
You could explicitly initialize b in A's constructor's initialization list, for example
class A {
B b; // private
public:
A : b() {} // the compiler provides the equivalent of this if you don't
};
However, b would get instantiated automatically anyway. The above makes sense if you need to build a B with a non-default constructor, or if B cannot be default initialized:
class A {
B b; // private
public:
A : b(someParam) {}
};
It may be impossible to correctly initialize in the constructor's initialization list, in which case an assignment can be done in the body of the constructor:
class A {
B b; // private
public:
A {
b = somethingComplicated...; // assigns new value to default constructed B.
}
};
You have created an instance of b in line 3. This line is enough so that B's constructor is called. If you have code like this
class A {
private:
B *b;
public:
A() {
//instantiate b here?
}
};
then it would make sense to instantiate b in A's constructor like
A()
{
b = new B();
}
The correct phase your looking for is "C++ initialization list". This initialization list is called/initialized before the constructor is called
In case of Default constructor, compiler equvalient constructor will be A() : B() {}
A very good reference
http://www.cprogramming.com/tutorial/initialization-lists-c++.html
At line 3, it is simply a declaration of B. However somewhere in your code where you have:
A a;
or
A a();
This calls the constructor of A. The internal b private member is full or garbage, as in not initialized. You are correct in that you can and probably should initialize member variable during construction where possible. There are two ways to do this:
A ()
{
b = B ();
}
Like you said:
or
A () : b (B())
{
}
The second version (initialization list) is slightly more efficient since it creates the new B object directly inside b. Whereas the first version creates a temporary and then moves that into b. This is the case when you initialize members from passed in parameters anyway (for non built in types). I'm making an assumption its the same in this case, but someone will be able to clarify.