Constructor confusion - c++

I always think I know C++ pretty well, but sometimes I'm surprised by even the most fundamental things.
In the following scenario, I'm confused as to why the constructor Derived::Derived(const Base&) is invoked:
class Base
{ };
class Derived : public Base
{
public:
Derived() { }
Derived(const Base& b)
{
std::cout << "Called Derived::Derived(const Base& b)" << std::endl;
}
};
int main()
{
Derived d;
Base b;
d = b;
}
This outputs: Called Derived::Derived(const Base& b), indicating that the second constructor in Derived was invoked. Now, I thought I knew C++ pretty well, but I can't figure out why that constructor would be invoked. I understand the whole "rule of four" concept, and I would think that the expression d = b would do one of two things: Either it would 1) invoke the implicit (compiler-generated) assignment operator of Base, or 2) Trigger a compiler error complaining that the function Derived& operator = (const Base&) does not exist.
Instead, it called a constructor, even though the expression d = b is an assignment expression.
So why does this happen?

d = b can happen because b is converted to Derived.
The second constructor is used for automatic type conversion.
It's like d = (Derived) b
Derived isa Base, but Base isn'ta Derived, so it has to be converted before assignment.

assigning base to derived? perhaps you meant (a) by ref (b) or derived to base. This doesn't really make sense, but the compiler is correctly using your (non-explicit) constructor to convert the Base instance to a new Derived instance (which is subsequently assigned into d).
Use an explicut constructor to prevent this from happening automatically.
Personally I think you messed up your code sample, because, normally assigning firstclass base to derived makes no sense without a conversion

There are two interacting features at play here:
Assignment Operators are never inherited
A constructor that is not explicit, or a conversion operator (operator T()) define a user-conversion that can be used implicitly as part of a conversion sequence
Assignement Operators are never inherited
A simple code example:
struct Base {}; // implicitly declares operator=(Base const&);
struct Derived: Base {}; // implicitly declares operator=(Derived const&);
int main() {
Derived d;
Base b;
d = b; // fails
}
From ideone:
prog.cpp: In function ‘int main()’:
prog.cpp:7: error: no match for ‘operator=’ in ‘d = b’
prog.cpp:2: note: candidates are: Derived& Derived::operator=(const Derived&)
Conversion sequence
Whenever there is an "impedance" mismatch, such as here:
Derived::operator= expects a Derived const& argument
a Base& is provided
the compiler will try to establish a conversion sequence to bridge the gap. Such a conversion sequence may contain at most one user-defined conversion.
Here, it will look for:
any constructor of Derived that can be invoked with a Base& (not explicit)
a conversion operator in Base that would yield a Derived item
There is no Base::operator Derived() but there is a Derived::Derived(Base const&) constructor.
Therefore our conversion sequence is defined for us:
Base&
Base const& (trivial)
Derived (using Derived::Derived(Base const&))
Derived const& (temporary object bound to a const reference)
And then Derived::operator(Derived const&) is called.
In action
If we augment the code with some more traces, we can see it in action.
#include <iostream>
struct Base {}; // implicitly declares Base& operator(Base const&);
struct Derived: Base {
Derived() {}
Derived(Base const&) { std::cout << "Derived::Derived(Base const&)\n"; }
Derived& operator=(Derived const&) {
std::cout << "Derived::operator=(Derived const&)\n";
return *this;
}
};
int main() {
Derived d;
Base b;
d = b;
}
Which outputs:
Derived::Derived(Base const&)
Derived::operator=(Derived const&)
Note: Preventing this ?
It is possible, in C++, to remove a constructor for being used in conversion sequences. To do so, one need to prefix the declaration of the constructor using the explicit keyword.
In C++0x, it becomes possible to use this keyword on conversion operators (operator T()) as well.
If here we use explicit before Derived::Derived(Base const&) then the code becomes ill-formed and should be rejected by the compiler.

Since you've defined a constructor for Derived which takes type Base and you are down-casting Base, the compiler chooses the most suitable constructor for the upcast, which in this case is the Dervied(const Base& b) you've defined. If you did not define this constructor you would actually get a compiling error when trying to make the assignment. For more info, you can read the following at Linuxtopia.

It can't assign value of different type, so it should first construct a Derived temporary.

Related

Would a derived class ever have an implicit copy constructor or assignment operator when it's deleted in the base class?

Qt defines Q_DISABLE_COPY as follows:
#define Q_DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
Q_DISABLE_COPY is used in the QObject class, but the documentation for it says that it should be used in all of its subclasses as well:
when you create your own subclass of QObject (director or indirect), you should not give it a copy constructor or an assignment operator. However, it may not enough to simply omit them from your class, because, if you mistakenly write some code that requires a copy constructor or an assignment operator (it's easy to do), your compiler will thoughtfully create it for you. You must do more.
But consider this program:
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1); // error: call to implicitly-deleted copy constructor of 'Derived'
Derived d3;
d3 = d1; // error: object of type 'Derived' cannot be assigned because its copy assignment operator is implicitly deleted
}
The errors from trying to compile that program seem to indicate that the compiler will not create copy constructors or assignment operators in derived classes when they're deleted in base classes. Is Qt's documentation just wrong about this, or is there some edge case when it would create them?
Related, but not a duplicate: Repeating Q_DISABLE_COPY in QObject derived classes. It gives reasons why it may be useful to use Q_DISABLE_COPY in a class even if it wouldn't be copyable anyway, but doesn't confirm that it never will in fact be copyable without it.
Since the base class copy constructor is deleted, the derived class has no way to know how to copy the base class object. This will disable any implicit copy constructors provided by the compiler.
From cppreference:
The implicitly-declared or defaulted copy constructor for class T is
defined as deleted if any of the following conditions are true:
T has direct
or virtual base class that cannot be copied (has deleted,
inaccessible, or ambiguous copy constructors)
T has direct or virtual
base class with a deleted or inaccessible destructor;
Inheriting Q_DISABLE_COPY can be useful when the user inherits from a class that deletes the default copy constructor, but provides their default implementation to override it.
struct Base {
Base() = default;
private:
Base(const Base &) = delete;
Base &operator=(const Base &) = delete;
};
struct Derived : Base {
Derived() = default;
Derived(const Derived&) : Derived() {}
Derived &operator=(const Derived&) {
return *this;
}
};
struct MoreDerived : Derived {};
int main() {
Derived d1;
Derived d2(d1); // Works fine!
Derived d3;
d3 = d1; // Works fine!
MoreDerived md1;
MoreDerived md2(md1); // Works fine!
MoreDerived md3;
md3 = md1; // Works fine!!
}
Edit: As #SR_ rightly states, in the above implementation of Derived, Base is not being copy constructed. I just wanted to illustrate the fact that it is easy to introduce an unintentional copy constructor when another class is modified in the inheritance hierarchy.
Prior to commit a2b38f6, QT_DISABLE_COPY was instead defined like this (credit to Swift - Friday Pie for pointing this out in a comment):
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
And Q_DECL_EQ_DELETE like this:
#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif
Q_COMPILER_DELETE_MEMBERS got defined if C++11 support (or at least a new enough draft of it to support = delete) was available.
Thus, if you compiled Qt back then against a C++03 compiler, it would instead have compiled something like this:
struct Base {
Base() {};
private:
Base(const Base &);
Base &operator=(const Base &);
};
struct Derived : Base {};
int main() {
Derived d1;
Derived d2(d1);
Derived d3;
d3 = d1;
}
And compiling that with g++ -std=c++03 gives you these errors:
<source>: In copy constructor 'Derived::Derived(const Derived&)':
<source>:9:8: error: 'Base::Base(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:5:5: note: declared private here
5 | Base(const Base &);
| ^~~~
<source>: In function 'int main()':
<source>:13:18: note: synthesized method 'Derived::Derived(const Derived&)' first required here
13 | Derived d2(d1);
| ^
<source>: In member function 'Derived& Derived::operator=(const Derived&)':
<source>:9:8: error: 'Base& Base::operator=(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:6:11: note: declared private here
6 | Base &operator=(const Base &);
| ^~~~~~~~
<source>: In function 'int main()':
<source>:15:10: note: synthesized method 'Derived& Derived::operator=(const Derived&)' first required here
15 | d3 = d1;
| ^~
So back then, "your compiler will thoughtfully create it for you" was technically true but not practically so, since the compiler creating it would cause compilation to fail, just with a different (and arguably less clear) error. I'm now convinced that it's not true at all anymore now that = delete is unconditionally used, so I plan to ask Qt's maintainers to remove/reword that section of their documentation.

Inheritance of Overloaded assignment Operator

C++ Complete Reference says ,"Except for the = operator,operator functions are inherited by a derive class."
But i cannt understand the behaviour of the following code:
#include<iostream>
using namespace std;
int main(){
class b{
int i;
public:
int operator=(b parm){
cout<<"base overload";
};
};
class d: public b{
int j;
public:
};
b inst1,inst11;
d inst2,inst22;
int a;
inst1=inst11; //works because assignment operator is overloaded for b
inst2=inst22; //If =operator function is not inherited then why does it output "base oberload"
inst1=inst2; //works because assignment overloaded for b
// inst2=inst11; //But if b was inherited then this should also work but it doesnt
}
I am expecting two output statements "base overload" but it is outputting three Why?? this is driving me nuts
operator= is not inherited. But the compiler will generate implicitly an operator= for class d, which invokes b::operator= for the assignment of the base subobject.
the operator performs member-wise copy assignment of the object's bases and non-static members, in their initialization order, using built-in assignment for the scalars and copy assignment operator for class types.
then
inst2=inst22; //If =operator function is not inherited then why does it output "base oberload"
The generated d::operator= is called here; inside which b::operator= is invoked.
inst1=inst2; //works because assignment overloaded for b
b::operator= is called here, it expects b as the argument and inst2 could be implicitly converted to the base class b.
// inst2=inst11; //But if b was inherited then this should also work but it doesnt
d::operator= is tried to be called here, it expects d as the argument but inst11 can't be implicitly converted to the derived class d.

C++ non-static data member initializers, just a little bit confused

I am a little bit confused about why the following code does what it does:
class Base
{
public:
Base() = default;
Base(const Base &) =delete;
Base &operator=(const Base &) = delete;
Base(const char*) {}
};
class Holder
{
public:
Holder() = default;
private:
// Base b = Base();
Base b2 = {};
};
int main()
{
Holder h;
}
in this incarnation, it compiles, however if I un-comment Base b = Base(); it gives the following error:
main.cpp:15:17: error: use of deleted function 'Base::Base(const Base&)'
Base b = Base();
^
main.cpp:5:6: note: declared here
Base(const Base &) =delete;
^
and I am just unable to find in the standard why it tries to call the copy constructor for the Base b = Base() initializer, and why doesn't it call for the Base b2 = {} ... or is this just one of those little obscurities that is hidden in a few words in a paragraph somewhere?
Can you please give a (short) explanation why this happens?
(coliru: http://coliru.stacked-crooked.com/a/c02ba0293eab2ce5 )
That's because, conceptually, that line constructs from Base(), which requires a copy/move constructor. The probable reason why you weren't aware of this, is because that expression generally triggers copy elision: a standard optimization. It's one of those C++ gotcha's.
(31.3) — when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move.
As for why Base b2 = {} works, see
(3.4) — Otherwise, if the initializer list has no elements and T is a
class type with a default constructor, the object is
value-initialized.
You could just do Base b;.
T object = {arg1, arg2, ...}; is syntax for list initialization. There is no copying involved.
T object = T() is not list initialization. The right hand operand constructs a value-initialized temporary, and object is move- or copy-initialized from it. The move and copy can be elided, but the type must be movable or copyable, otherwise this is not allowed.
The true reason is because you forget keyword "explicit", you should change the code
Base(const Base &) =delete;
To:
explicit Base(const Base &) = delete;
Let me explain the reason, In class "Holder", Base b = Base() will:
Base() will generate a temporary object by calling constructor "Base()"
However, constructor "Base(const Base&)" has a single parameter and without keyword "explicit", this will make code:
Base obj = value;
become
Base obj(value);
Therefore, just add keyword "explicit" will solve this problem. ^_^

Effect of `explicit` on static upcasting

May an instance of a derived class be implicitly converted to an instance of its base class, when the only candidate constructor in the base class is marked explicit?
I ran this:
struct Base {
Base() {}
explicit Base(Base const& b) {}
};
struct Derived : Base {};
int main() {
Derived d;
Base b = d;
}
And got this:
error: no matching function for call to 'Base::Base(Derived&)'
Then I ran this:
struct Base {
Base() {}
Base(Base const& b) {}
};
struct Derived : Base {};
int main() {
Derived d;
Base b = d;
}
And got no errors.
But I'm not entirely convinced that this test succeeds due to explicitness rather than ctor synthesis. In particular, I didn't think that explicit cared about the type of arguments, but that it would force me to write Base b = static_cast<Base>(d) ... which I'm not doing in either case.
It's not the conversion that fails. Copy-initialization requires an accessible copy-constructor.
This also fails:
struct Base {
Base() {}
explicit Base(Base const& b) {}
};
int main() {
Base d;
Base b = d;
}
In hindsight, it seems clear.
The elements at play here are:
The only would-be-synthesised candidate constructor is Base(Base const&), and I don't provide a constructor Base(Derived const&).
Said constructor is explicit, but I provide no explicit conversion.
So, the answer is "no".
This:
int main() {
Derived d;
Base b = d;
}
is not upcasting. That is creating a new object, called b, which contains a copy of the value of d. In order to have upcasting, you must be using polymorphic values (references or pointers). Upcasting would therefore be:
int main() {
Derived d;
Base &b = d;
}
The variable b contains a reference to the Base part of d. If Base had some public member int baseValue;, then b.baseValue refers to the exact same data as d.baseValue. For example:
int main() {
Derived d;
Base &b = d;
d.baseValue = 10;
cout << b.baseValue << endl;
}
This will write 10. If b weren't a reference, but a regular object, it would have copied the (initialized) value from d before d's value was changed. And therefore changing one would not change the other.
The purpose of the explicit keyword is to prevent syntax like Base b = d from working. If you make a constructor explicit, you are saying, "I don't want the compiler to ever implicitly call this copy constructor. If the user is to use it, then they must explicitly say so." If you want implicit conversion of types, then you must say so.

c++ is this copy constructor?

class A {};
class B { public: B (A a) {} };
A a;
B b=a;
I read this from http://www.cplusplus.com/doc/tutorial/typecasting/ . It says this is a implicit type conversion. From class A to class B.
I want to ask, is this also an example of copy constructor?
Thanks.
No, it's not a copy constructor. A copy constructor copies one object of one type into another of the same type:
B::B(const B& b)
{
// ...
}
As a side note, if you need a copy constructor then you also need a destructor and an assignment operator, and probably a swap function.
What B::B(A) is is a conversion function. It's a constructor that allows you to convert an object of type A into an object of type B.
void f(const B& obj);
void g()
{
A obja;
B objb = obja;
f(obja);
}
No, A copy constructor has the form
class A
{
public:
A(const A& in) {...}
}
No, a copy constructor is called when you create a new variable from an object. What you have there is two objects of different types.
The line B b = a; implies that a copy constructor is used, as if you had typed B b = B(a); or B b((B(a)));. That is, the compiler will check whether B has an accessible (public) copy constructor - whether user-defined or the default one provided by the compiler. It doesn't mean, though, that the copy constructor has to be actually called, because the language allows compilers to optimize away redundant calls to constructors.
By adding a user-defined copy constructor to B and making it inaccessible, the same code should produce a compiler error:
class A {};
class B {
public:
B (A ) {}
private:
B (const B&) {} // <- this is the copy constructor
};
A a;
B b=a;
For example, Comeau says:
"ComeauTest.c", line 10: error: "B::B(const B &)" (declared at line 6), required
for copy that was eliminated, is inaccessible
B b=a;
^