Copy-constructor inheritance - c++

struct A{
virtual void what() { cout << "Base" << endl; };
};
struct B : A {
virtual void what() { cout << "Sub" << endl; };
int m;
B() : m(10) {};
B(const A & x) : m(50) {};
};
void main() {
B b1;
B b2 = b1;
cout << "Number: "
<< b2.m << endl;
};
Why isn't b2.m = 50? I'm trying to copy a b-object and i have the copy constructor B(const A & x) : m(50). Do i need need to make a copy c'tor for the derived class ass well ? Like B(const B&x) ?? I thought since a b-object has an a part, we could use B(const A & x) : m(50) instead of the default constructor: :S
In the case where you have a function with the parameter of an A object, you can send in a B object. How come it differs with the copy constructor?

The reason is that B(const A& x) is not a copy-ctor — copy constructor for type T must always take an lvalue reference to T as the first (and have no other non-default arguments) argument. Your class has no defined copy-ctor, so the compiler generates the default one, which does a member-wise copy, hence why b2.m is the same as b1.m.
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).

Copy constructors need to be of the same type.
You haven't actually made a copy constructor. B(const A & x) is just a constructor that takes a const A by reference.
So, you don't need to make one for the derived class "as well", but "at all". As you stated, the type of this will be B(const B &x).

The default copy-ctor for classes of type B would be B(const B&). Since you haven't specified this one, the compiler kindly generates it for you.
Its different with user defined methods as those cannot be compiler generated.

Related

Factory requires only declaration of copy ctor without implementation

I'm experiencing behavior which I don't understand in a copy constructor of derived class.
class A {
A(const A&);
public:
A() = default;
};
class B : public A {
friend class Factory;
B(const int v) : A(), m_test_val(v) {}
public:
int m_test_val;
B(const B&); // no implementation, just declaration
};
class Factory {
public:
static B create(const int v) {
return B(v);
}
};
int main() {
B b = Factory::create(2);
std::cout << b.m_test_val << '\n';
return 0;
}
The behavior I don't understand is a matter of a working copy constructor B::B(const B&); which, however, does not have any implementation.
When I use B::B(const B&) = default; instead, I get an error saying I'm using deleted function (implicitly deleted because of ill-formation) in the return statement of the Factory::create() function (The A::A(const A&) is private and without implementation on purpose).
And of course, when I use B::B(const B&) = delete;, compiler tells me I use a deleted function.
How is it possible that the copy constructor works with no implementation just with declaration?
Note: The example code is based on a much larger code that behaves the same way, hopefully I didn't leave something out.
The actual copy is elided by the compiler, which is allowed since the copy constructor is accessible. The compiler is of course under no obligation to elide this copy and if it didn't I would expect a linker error not finding the implementation of the copy constructor.

How to initialize parameters / return values whose type has no copy constructor and only explicit constructors

How to initialize function parameters or function return values when the type has no copy constructor and only explicit constructors? For example:
struct A { };
struct B {
B(const B&) = delete;
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return ...; // how to return a?
}
int main() {
A a;
foo( ... ); // how to pass a?
}
return a / foo(a) does not work because constructor B(const A&) is explicit. return B(a) / foo(B(a)) does not work because copy constructor is deleted.
My intention is to know the language rules. To me it looks like a flaw in the language that, considering the presented type B, I can initialize locals, statics and class members but apparently neither function parameters nor function return values.
B(const B&) = delete;, without an explicit definition of move-constructor, means the object cannot be copied or moved.
This means that you cannot pass by value, either as a parameter or as a return.
The explicit constructor is a red herring.
If you don't want to change B to enable copying or moving, then you will have to pass by reference.
answers in annotated code below:
struct A { };
struct B {
B(const B&) = delete;
B(B&&) = default; // how to return B - pt 1 : move constructor
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return B(a); // how to return B - pt 2 : explcitly convert it
}
int main() {
A a;
foo(B(a)); // how to pass a - explicit conversion
}
I think your issue is a misconception:
You do not initialize function parameters or return values with the constructor.
Instead you hand in parameters to a function, handing in means copying them.
Depending on the function signature either the variable itself is copied (void foo(B b)) or the reference/pointer to it is copied.
In the first case you need a copy constuctor for your type, while you do not need one for references/pointers.
The same is true for returning values (!) from a function.
You also need a copy constructor there.
But you deleted the copy constructor, thus the compile errors.
With C++11 move semantics were added as an optimisation opportunity.
When a move constructor is available r-values will be moved in/out of functions automatically instead of copied.
Returning a local variable will also move it out instead of copying.
Since you declared a copy consturctor (no matter if as deleted/default) no move related functions will be created by the compiler.
And thus also the automatic moving fails.

Copy-constructor from base class

I have the following code:
#include <iostream>
#include <utility>
class A {
public:
A() { }
A(const A&, int i) { std::cout << "A copy" << std::endl; }
A(A&&, int i) { std::cout << "A move" << std::endl; }
A(const A&) = delete;
A(A&&) = delete;
};
class B : public A {
public:
B(int i) { std::cout << "B construct" << std::endl; }
B(const A& a) : A(a, 1) { std::cout << "B copy" << std::endl; }
B(A&& a) : A(std::move(a), 1) { std::cout << "B move" << std::endl; }
};
B make_b() {
return B(1);
}
int main() {
B b = make_b();
}
The compiler error reports the error that B cannot be copy-constructed (for return from make_b), because it has no copy-constructor, because A's copy-constructor is deleted.
Does B(const A&) not qualify as copy-constructor, and what is the rule that applies here?
Does the copy and move constructor always have to take one argument of the same type (and not a superclass)?
Can it have additional parameters with default values?
Can it be templated so that it can resolve to a valid copy constructor?
To allow implicit copy and move construction, is it necessary to explicitly add copy and move-constructors B(const B&) and B(B&&)?
Does B(const A&) not qualify as copy-constructor, and what is the rule that applies here?
No. A copy constructor creates another object of the same type. A is not the same type as B. If you try to construct an object of a derived class from an object of its base class, how are you supposed to initialize the derived class' members? The source object you are copying from doesn't have those members to copy!
Furthermore, B already has a copy constructor, implicitly declared by the compiler, but because the implicit definition would be ill-formed (because the base class A is not copyable) it is deleted by the compiler, so you cannot use it.
Does the copy and move constructor always have to take one argument of the same type (and not a superclass)?
Not necessarily one argument, B(const B&, int = 0) is a copy constructor, because it can be called to create a copy of a B. But B(const A&) is not a copy constructor.
Can it have additional parameters with default values?
Yes.
To allow implicit copy and move construction, is it necessary to explicitly add copy and move-constructors B(const B&) and B(B&&)?
Yes, you need to define them explicitly, because the implicit definitions the compiler would use won't work.
Since your derived type doesn't have any members, and you already have constructors that take an A, you could define them like so:
B(const B& b) : B(static_cast<const A&>(b) { }
B(B&& b) : B(static_cast<A&&>(b) { }
This creates delegating constructors which simply forward the argument to your existing constructors (using suitable casts to the base type).
About 1 :
when constructing , the compiler calls all the base classes one by one from the highest until the currently-constructed-class.
if C inherits from B inherits from A
the compiler calls A() then B() than C() ctros in order to build C object.
the same goes for copy constructors:
in your example , you called for A() copy constructor to build the "A" part of the object , but you deleted it .
the problem here is returing B by value, which calls first move ctor if exits , and move ctor if not. you deleted both

Forwarding constructors calls 2 times copy constructor of base class

The following code forwards constructors from base to derived class.
Why 2 copy constructor calls? What happen in the background?
Compiled with g++.
#include <iostream>
using namespace std;
struct A {
A() { cout << "A" << endl; }
A(const A&) { cout << "A(const A&)" << endl; }
template<typename T> A(T a); // Needed to compile :-O
};
template<typename T>
struct C : public T { using T::T; };
int main()
{
A a;
C<A> ca(a);
//C<A> caa(ca);
return 0;
}
Output is:
A
A(const A&)
A(const A&)
By defining a constructor template in A, C will be given a constructor template with a similar signature. It is implicitly defined similarly to:
template<typename T>
struct C : public T
{
//using T::T;
C() = default;
C(C const&) = default;
template<typename U> C(U a) : T( std::forward<U>(a) ) {}
};
This now calls the copy-constructor of A twice: once for taking the argument by value. The second call results from T( std::forward<U>(a) ) calling the copy-ctor of A. This was surprising to me, as you'd expect an inherited ctor to call the exact ctor of the base class of which it has been inherited. But that's not the case, overload resolution selects not the ctor template of A, but the plain copy-ctor A(A const&) (see below).
Interestingly, it doesn't care much what the constructor template in A does, it only needs to be declared. That's why in the OP, the definition can be missing; it can also be deleted (which might be a defect?).
The copy-ctor of A only has to be chosen during overload resolution of the initialization T( std::forward<U>(a) ). This is the case here: The argument is an rvalue of type A, which can bind directly to a const A& reference, as required by the copy-ctor of A. As the reference binding is direct and w/o derived-to-base conversion, the copy-ctor ranks as an Exact Match. The ctor template in A is ranked as an Exact Match as well, but as there's a template- and non-template function with the same rank in the overload set, the non-template function is preferred (the copy-ctor A(A const&)).
One call to A::A(const A&) is the base class constructor of C<A>.
The other is called to copy the pass-by-value parameter.

initialization ignores constructor templates

While pursuing some errors, I stumbled upon the following behavior of initialization, which seems odd to me: While initialization checks for existing constructors, there seem to be cases were templates for fitting constructors are ignored. Consider for example the following program:
#include <iostream>
template<class T>
struct A {
A() {};
template<class S>
A(const A<S>& a) {std::cout << "constructor template used for A" << std::endl;};
};
template<class T>
struct B{
B() {};
B(const B<int>& b) {std::cout << "constructor used for B" << std::endl;};
};
int main() {
A<int> a;
B<int> b;
A<int> aa = a;
B<int> bb = b;
A<double> aaa = a;
}
For me, this produces the output
constructor used for B
constructor template used for A
this means it does not use the constructor in the third line of main. Why not? Is there a reason? Or is my syntax off somewhere? The template seems to work, as it is successfully used in the last line.
I know the example seems overly complicated, but various simplifications made the behavior I wanted to display go away. Also: A template specialization will be used by the initialization and is how I currently prevent this causing errors (where it caused errors in the first place).
I am sorry if my question is off in any way, I am not a programmer, I am not a native speaker and this is my first question, please forgive me.
The compiler provides an implicitly declared non-template copy constructor with signature equivalent to
A(const A& a);
because a template constructor is not considered as a user defined copy constructor, i.e. a copy constructor has to be a non-template.
The implicitly declared copy constructor is a better match in the overload resolution than the template version, and is the one that gets called when you copy construct an A<T> from an A<T>. This can be illustrated with a simple example, with a user defined A(const A&):
#include <iostream>
template<class T>
struct A
{
A() {};
A(const A& a) {
std::cout << "copy constructor used for A" << std::endl;
}
template<class S>
A(const A<S>& a) {
std::cout << "constructor template used for A" << std::endl;
}
};
int main()
{
A<int> ai;
A<double> ad = ai; / calls template conversion contructor
A<int> ai2 = ai; // calls copy constructor A(const A&);
}
Per Paragraph 12.8/7 of the C++11 Standard:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
Moreover, per Paragraph 12.8/2:
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&,
volatile X& or const volatile X&, and either there are no other parameters or else all other parameters
have default arguments (8.3.6).
Therefore, the compiler will generate an implicit copy constructor here that gets invoked during the execution of this line:
A<int> aa = a;
This explains why you do not see a corresponding output.
This is because templated ctor is not a copy ctor. See also https://stackoverflow.com/a/1249848/341065.