The title is only one of a few things I am confused on about the following example:
struct A {
A() {
std::cout << "Default constructor of A" << std::endl;
}
A(const A &) {
std::cout << "Copy constructor of A" << std::endl;
}
};
struct B : private A {
// using A::A; // does not help for question 2.
};
int main() {
A a;
B b;
B c(b); // Does not work with `a` as an argument
return 0;
}
This example outputs:
Default constructor of A
Default constructor of A
Copy constructor of A
Questions:
B
are available in main
? This question is similar to the one in this post, but there the question was about using that constructor from inside of B
, which is different.const A &
argument. However, if I write B c(a)
instead of B c(b)
, the code does not compile. How come? (Please note that un-commenting the using
directive in B
does not help).a
and c
? Question 2 has been taken to a different post.
You are not calling the constructor of A directly, the default constructor of B is doing that for you. If this worked any differently you would never be able to construct any class that inherits anything privately.
This is because B has no copy constructor for type A. The only constructor that could apply here is the (default) copy constructor of type B, which takes a B as an argument.
Constructors are bound to their class, they do not inherit in the same sense as functions do. How to explain... The basic goal is to ensure that each class always constructs completely. Thus, to construct an A of any kind (whether standalone, or as part of B), a constructor of A must run. Similarly, to construct an object of class B, a constructor of B must run. If you 'inherit' the A::A() constructor, you would be able to construct B's that were not completely constructed. In that case the A::A() constructor has not run any part of the construction sequence for B, leaving B in an invalid state.
Let's try some different source. We keep A as it is, and change B like this:
struct B : private A {
B () { val = 42; }
void foo () { if (val != 42) abort (); }
using A::A;
int val;
};
Now let's say we, uhh, acquire a B without constructing it:
B b (a); // illegal, but for the sake of argument.
b.foo ();
We have specified that we construct this B using the A::A constructor, so the only code that will be executed is A::A(). In particular, B::B() is not executed, so val will have whatever value was present on the stack at the time. The chance of it being 42 is 1 in 2^32, in other words, not very likely.
What will happen when B.foo() is called? The object is not in a valid state (val is not 42), so the application aborts. Oops!
This is of course a contrived example, but it shows that using a non-constructed object is a very bad thing, and therefore the language prevents you from creating any such objects.
This design pattern is seen many times in C++, for example in std::mutex. Just declaring a lock is sufficient to do the locking and unlocking, but there is no need to refer to the lock after the declaration. Since it is a common design pattern a warning would be inappropriate.
Only if the compiler can prove that constructing and destructing a local variable has no side effects, and finds that it is not being used, you may see a warning (depending on the compiler).