Consider the code below running C++11. If I understand move semantics correctly, the copy constructor should not be called. But it is. Can someone explain why?
template<class D>
struct traced
{
public:
traced() = default;
traced(traced const&) { std::cout << typeid(D).name() << " copy ctor\n"; }
protected:
~traced() = default;
};
class A : public traced<A>{
public:
A(int x) : x_(x) {}
private:
int x_;
};
int main() {
// I thought the following two are equivalent. Apparently not.
aList.push_back(A(6)); // Prints out ".. copy ctor" ..
aList.emplace_back(6); // Does not print out " ... copy ctor"
}
aList.push_back(A(6));
This constructs a temporary A and moves it into the container. The implicitly generated move constructor of A is called, which needs to construct the base traced<A> from the base subobject of the temporary. However, trace explicitly declares a copy constructor, so it doesn't have move constructor by default, and A's move constructor nonetheless needs to perform a copy for its base class subobject.
aList.emplace_back(6);
This constructs an A directly into the container. No copy or move of any sort is involved.
Related
This question already has answers here:
Why does the implicit copy constructor calls the base class copy constructor and the defined copy constructor doesn't?
(3 answers)
Closed 11 months ago.
Consider the following example:
class A
{
public:
A()
{
cout<<"constructor A called: "<<this<<endl;
};
A(A const& other) = default;
};
class B : public A
{
public:
B()
{
cout<<"constructor B called: "<<this<<endl;
};
//This calls A's constructor
B(B const& other)
{
cout<<"B copy constructor called: "<<this<<endl;
};
//While this doesn't call A's constructor
//B(B const& other) = default;
};
int main()
{
B b;
B b2(b);
cout<<"b: "<<&b<<endl;
cout<<"b2: "<<&b2<<endl;
return 0;
}
Output:
constructor A called: 0x7fffc2fddda8
constructor B called: 0x7fffc2fddda8
constructor A called: 0x7fffc2fdddb0
B copy constructor called: 0x7fffc2fdddb0
b: 0x7fffc2fddda8
b2: 0x7fffc2fdddb0
Why is the constructor of A is called when copying B?
Shouldn't the copy constructor of A be called instead?
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
It will be nice if someone can give a reasonable explanation as to why.
Why is the constructor of A is called when copying B? Shouldn't the copy constructor of A be called instead?
No, it shouldn't.
A derived class must always initialize a base class. If the derived class has a constructor that is implemented explicitly by the user, but it does not explicitly call a base class constructor in its member initialization list, the compiler will make an implicit call to the base class's default constructor, regardless of the type of the derived constructor. The compiler does not make any assumption about the user's intent in implementing the derived constructor. If the user wants a specific base class constructor to be called, they need to make that call themselves.
Since B has an explicitly implemented copy constructor that lacks a member initialization list, the compiler initializes A by calling its default constructor, not its copy constructor.
IOW, this:
B(B const& other)
{
...
}
Is equivalent to this:
B(B const& other) : A()
{
...
}
NOT to this, as you are thinking:
B(B const& other) : A(other)
{
...
}
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
Correct, a default'ed copy constructor will call the base class's copy constructor, not its default constructor. The compiler is implicitly implementing the entire derived constructor, and so it will choose the appropriate base class constructor to call.
I'm new to C++ and need some help on move constructors. I have some objects that are move-only, every object has different behaviours, but they all have a handle int id, so I tried to model them using inheritance, here's the code
#include <iostream>
#include <vector>
class Base {
protected:
int id;
Base() : id(0) { std::cout << "Base() called " << id << std::endl; }
virtual ~Base() {}
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
Base(Base&& other) noexcept = default;
Base& operator=(Base&& other) noexcept = default;
};
class Foo : public Base {
public:
Foo(int id) {
this->id = id;
std::cout << "Foo() called " << id << std::endl;
}
~Foo() { std::cout << "~Foo() called " << id << std::endl; }
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
Foo(Foo&& other) noexcept = default;
Foo& operator=(Foo&& other) noexcept = default;
};
int main() {
std::vector<Foo> foos;
for (int i = 33; i < 35; i++) {
auto& foo = foos.emplace_back(i);
}
std::cout << "----------------------------" << std::endl;
return 0;
}
Each derived class has a specific destructor that destroys the object using id (if id is 0 it does nothing), I need to define it for every derived type. In this case, the compiler won't generate implicitly-declared copy/move ctors for me, so I have to explicitly make it move-only to follow the rule of five, but I don't understand what the =default move ctor does.
When the second foo(34) is constructed, vector foos reallocates memory and moves the first foo(33) to the new allocation, however, I saw that both the source and target of this move operation has an id of 33, so after the move, foo(33) is destroyed, leaving an invalid foo object in the vector. In the output below, I also didn't see a third ctor call, so what on earth is foo(33) being swapped with? a null object that somehow has an id of 33? Where does that 33 come from, from copy? but I've explicitly deleted copy ctor.
Base() called 0
Foo() called 33
Base() called 0
Foo() called 34
~Foo() called 33 <---- why 33?
----------------------------
~Foo() called 33
~Foo() called 34
Now if I manually define the move ctor instead:
class Foo : public Base {
public:
......
// Foo(Foo&& other) noexcept = default;
// Foo& operator=(Foo&& other) noexcept = default;
Foo(Foo&& other) noexcept { *this = std::move(other); }
Foo& operator=(Foo&& other) noexcept {
if (this != &other) {
std::swap(id, other.id);
}
return *this;
}
}
Base() called 0
Foo() called 33
Base() called 0
Foo() called 34
Base() called 0 <-- base call
~Foo() called 0 <-- now it's 0
----------------------------
~Foo() called 33
~Foo() called 34
this time it's clearly swapping foo(33) with a base(0) object, after id 0 is destroyed, my foo object is still valid. So what's the difference between the defaulted move ctor and my own move ctor?
As far as I understand, I almost never need to manually define my move ctor body and move assignment operator unless I'm directly allocating memory on the heap. Most of the time I'll only be using raw data types such as int, float, or smart pointers and STL containers that support std::swap natively, so I thought I would be just fine using =default move ctors everywhere and let the compiler does the swap memberwise, which seems to be wrong? Perhaps I should always define my own move ctor for every single class? How can I ensure the swapped object is in a clean null state that can be safely destructed?
What does explicitly-defaulted move constructor do?
It will initialize its member variables with the corresponding member variables in the moved from object after turning them into xvalues. This is the same as if you use std::move in your own implementation:
Base(Base&& other) noexcept : id(std::move(other.id)) {}
Note though that for fundamental types, like int, this is no different from copying the value. If you want the moved from object to have its id set to 0, use std::exchange:
Base(Base&& other) noexcept : id(std::exchange(other.id, 0)) {}
Base& operator=(Base&& other) noexcept {
if(this != &other) id = std::exchange(other.id, 0);
return *this;
}
With that, the Foo move constructor and move assignment operator can be defaulted and "do the right thing".
Suggestion: I think Base would benefit from a constructor that can initialize id directly. Example:
Base(int Id) : id(Id) {
std::cout << "Base() called " << id << std::endl;
}
Base() : Base(0) {} // delegate to the above
Now, the Foo constructor taking an id as an argument could look like this:
Foo(int Id) : Base(Id) {
std::cout << "Foo() called " << id << std::endl;
}
What does explicitly-defaulted move constructor do?
A compiler generated move constructor moves each sub object.
so after the move, foo(33) is destroyed, leaving an invalid foo object in the vector.
The destruction of the moved-from "foo(33)" has no effect on the moved-to "foo(33)" object that remains in the vector; The shown destructor doesn't do anything that would cause the remaining object to become "invalid".
a null object that somehow has an id of 33?
'Null' is a state of a pointer. It doesn't have a conceptual meaning for a class instance - unless the class models a pointer wrapper, in which case you could consider the class instance to be conceptually null when the pointer that it wraps is null; but that doesn't apply to the example class.
Where does that 33 come from, from copy?
You initialised the int with 33; that's where the 33 comes from. The moved-to object also has 33, because it was moved from the integer that had the value 33.
In the output below, I also didn't see a third ctor call,
You didn't see output from the move constructor, because the defaulted move constructor doesn't output anything.
So what's the difference between the defaulted move ctor and my own move ctor?
The default move constructor initialises all sub objects by moving from each respective sub object of the source. By contrast, your move constructor default initialises the sub objects, and then delegates to the user defined move assignment which swaps id .
Maybe it would help, in order to understand the difference, to see what the default move constructor would look like if written manually:
Foo(Foo&& other) noexcept
: id{std::move(other.id)} {}
Perhaps I should always define my own move ctor for every single class?
No. Most of the time you should define none of the special member functions. This rule of thumb is know as rule of 0.
But remember that most of the time is not the same as all of the time. Sometimes - although rarely - you do need a user defined destructor and in those cases most of the time you also need user defined copy and move constructors and assignment operators. This is known as rule of 5 (aka rule of 3 before move semantics were introduced in the language).
How can I ensure the swapped object is in a clean null state that can be safely destructed?
The very first step is to specify what "clean null state" means.
If there are states that cannot be destroyed, then typically it's best to specify a class invariant that always holds as a post condition of all member functions, and ensures the safety at all times. Maintaining such invariant typically necessitates user defined special member functions.
I'm new to SO so let me know if I need to change anything. I did my best to be as thorough and provide example code. I know that many similar questions have been asked, I was unable to find one matching my specific problem though.
Furthermore, I'm aware that what I'm doing is not something one would do in 'real' code, I'm just trying to get a better understanding of r/l/p/x..values.
I have a base and a derived class, both having the default, copy, and move constructors. Now I want to have the copy constructor of the derived class calling the move constructor of the base class.
class Base
{
public:
Base(){ std::cout << "default base constructor called.\n"; }
Base(Base const &other) { std::cout << "copy base constructor called.\n"; }
Base(Base &&tmp) { std::cout << "move base constructor called.\n"; }
};
And basically the same for the derived class:
class Derived : public Base
{
public:
Derived(){ std::cout << "default derived constructor called.\n";}
Derived(Derived const &other)
:
Base(std::move(other)) // here I want to call Base(Base &&tmp)
{
std::cout << "copy derived constructor called.\n";
}
Derived(Derived &&tmp)
:
Base(std::move(tmp)) // correctly calls Base(Base &&tmp)!
{
std::cout << "move derived constructor called.\n";
}
};
So in my main function, I now want to call the copy constructor, which then calls the move constructor of the base class.
int main()
{
Derived der{};
Derived der_copy{ der };
Derived der_move{ std::move(der) };
}
The output I would get is this:
default base constructor called.
default derived constructor called.
copy base constructor called. <-- why not move?
copy derived constructor called.
move base constructor called.
move derived constructor called.
I was expecting the following:
default base constructor called.
default derived constructor called.
move base constructor called.
copy derived constructor called.
move base constructor called.
move derived constructor called.
So when I use std::move(tmp) in the derived move constructor (so on Base &&tmp) that the base move constructor is called, but when I use std::move(other) in the derived copy constructor (so on Base const &other) that the base copy constructor is called?
Tbh, this seems so strange that I'm afraid that I just made a mistake in my code, I checked everything multiple times but I can't seem to get the move base constructor called in the case above...
Thanks for your help!
In the copy constructor
Derived(const Derived& other)
std::move(other) will result in an xvalue expression of type const Derived&&.
This is a legal but somewhat weird type: std::move(other) is a temporary object, but you can't move from it, because it is constant. Such references have a limited number of use cases. See the declarations of std::as_const and std::ref for one particular example.
const Derived&& cannot bind to Base&&, that's why during the overload resolution between
Base(const Base&)
Base(Base&&)
the former is chosen by the compiler.
At the risk of getting undefined behaviour, you can cast constness away and write
Derived(const Derived& other) : Base(std::move(const_cast<Derived&>(other))) {}
to call the move constructor of Base. But don't do it in real code.
You need to change your Base class like this:
Base(const Base &&tmp) { std::cout << "move base constructor called.\n"; }
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
I have some questions about constructors in C++. For each question (from (1) to (4)) I would like to know if the behaviour is perfectly defined regarding to the standard.
A) The first one is about initialization of members :
class Class
{
public:
Class()
: _x(0)
, _y(_x)
, _z(_y) // <- (1) Can I initialize a member with other members ?
{
;
}
protected:
int _x;
int _y;
int _z;
};
B) What are the functions added to each class by the compiler ?
template<typename T> class Class
{
public:
template<typename T0>
Class(const Class<T0>& other)
{
std::cout<<"My copy constructor"<<std::endl;
_x = other._x;
}
template<typename T0 = T>
Class (const T0 var = 0)
{
std::cout<<"My normal constructor"<<std::endl;
_x = var;
}
public:
T _x;
};
// (2) Are
// Class(const Class<T>& other)
// AND
// inline Class<T>& operator=(const Class<T>& other)
// the only functions automatically added by the compiler ?
As an example, if I call :
Class<int> a;
Class<int> b(a); // <- Will not write "My copy constructor"
Class<double> c(a); // <- Will write "My copy constructor"
(3) Is this behaviour perfectly normal according to the standard ?
(4) Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?
Can I initialize a member with other members ?
Yes, as long as those other members have already been initialised; i.e. as long as their declarations come before the member being initialised.
Are [the copy constructor] and [the copy-assignment operator] the only functions automatically added by the compiler ?
It will also implicitly declare a destructor, which will destroy _x using its destructor.
In C++11, a move constructor (Class(Class&&)) and move-assignment operator (Class& operator=(Class&&)) are also implicitly declared, unless you declare a copy or move constructor, or a copy or move assignment operator.
Note that your constructor template is not a copy constructor, and the implicit one will be used instead:
Class<T1> t1;
Class<T1>(t1); // prints nothing
Class<T2>(t1); // prints "My copy constructor" (which is a lie)
Is this behaviour perfectly normal according to the standard ?
Yes, see chapter 12.
Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?
Yes, a default constructor will only be implicitly declared if you don't declare any constructors at all.