The following class outputs when a constructor is called:
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
private:
int val_;
};
This union allows me to skip the default constructor of A, which is desirable:
union MyUnion {
MyUnion() {}
A a;
};
My goal with main was to move-construct a. Instead it move-assigns the a.
int main(){
MyUnion my_union[2];
std::cout << "Entering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].a = std::move(A(i));
}
return 0;
}
An example output from the above main function is:
Entering for loop:
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b0
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b4
I wanted to move-construct, but I'm instead move-assigning. Move-assigning without move constructing is unsafe in general. The solution I came up with was to use std::construct_at. I also added a destructor. The full program with these modifications is below:
#include <iostream>
#include <memory>
#include <utility>
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
~A() {
std::cout << "Destructor called at " << this << "\n";
}
private:
int val_;
};
union MyUnion {
MyUnion() {
std::cout << "MyUnion Constructor called!\n";
}
void ConstructAt(A&& other){
std::construct_at(&a, std::forward<A>(other));
}
A a;
~MyUnion(){
std::cout << "MyUnion Destructor called!\n";
a.~A();
}
};
int main(){
MyUnion my_union[2];
std::cout << "\nEntering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].ConstructAt(std::move(A(i)));
}
std::cout << "\nExiting for loop:\n";
return 0;
}
This gives output:
MyUnion Constructor called!
MyUnion Constructor called!
Entering for loop:
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4840
Destructor called at 0x7ffe38dd4838
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4844
Destructor called at 0x7ffe38dd4838
Exiting for loop:
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4844
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4840
This appears to be what I want.
Question
Was this the correct way to call the move constructor? Am I using std::forward correctly? Is this the correct way to handle the destructor of a union?
Similar questions like mine have already been discussed in this community (there are several posts, like this, this, this, this and this), but the most interesting one (for what I would like to discuss here) is this, though it does not really solve my problem. What I would like to discuss is the following warning:
warning: defaulted move assignment for ‘UG’ calls a non-trivial move assignment operator for virtual base ‘G’.
In the last post mentioned, one user answered that this warning is saying that the base class can be moved twice and so
The second move assignment is from an already moved from object, which
could cause the contents from the first move assignment to be
overwritten.
I understand that this is a problem and it has better be avoided. Now, I have several classes inheriting from a purely virtual base class. Multiple inheritance is also involved, and is represented in the MWE below. What I would like to have is the possibility of using the move constructor and the move assignment operator whenever needed, so that I can do
T t3;
T t2 = std::move(t1);
t3 = std::move(t2);
without worrying about memory leaks, and everything being moved correctly. Presently, T t2 = std::move(t1); works fine, but t3 = std::move(t2); does not. I made a MWE, which represents very well my actual code, and I'm quite convinced that a solution for the MWE will also be a solution for my code. The MWE is:
class G {
public:
G() = default;
G(G&&) = default;
G(const G&) = default;
virtual ~G() = default;
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
return *this;
}
G& operator= (const G&) = default;
virtual void asdf() = 0; // abstract function to force complexity
string mem_G;
};
class UG : virtual public G {
public:
UG() = default;
UG(UG&& u) = default;
UG(const UG&) = default;
virtual ~UG() = default;
UG& operator= (UG&&) = default;
UG& operator= (const UG&) = default;
void asdf() { mem_G = "asdf"; }
string mem_UG;
};
class T : virtual public G {
public:
T() = default;
T(T&& t) = default;
T(const T&) = default;
virtual ~T() = default;
T& operator= (T&&) = default;
T& operator= (const T&) = default;
virtual void qwer() = 0;
string mem_T;
};
class FT : public UG, virtual public T {
public:
FT() = default;
FT(FT&& f) = default;
FT(const FT&) = default;
virtual ~FT() = default;
FT& operator= (FT&&) = default;
FT& operator= (const FT&) = default;
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
};
Using the classes in the example, the function
void test() {
FT c1;
c1.mem_G = "I am G";
c1.mem_UG = "I am UG";
c1.mem_T = "I am T";
c1.mem_FT = "I am FT";
cout << "c1" << endl;
cout << c1 << endl;
cout << "Move constructor" << endl;
FT c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
produces the output (without the comments, which I added for a better understanding of the output)
c1
mem_G: I am G
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
Move constructor // correct move of 'c1' into 'c2'
c1
mem_G:
mem_UG:
mem_T:
mem_FT:
c2
mem_G: I am G
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
Move assignment operator // moving 'c2' into 'c1' using the move operator will move G's memory twice
G& G::operator=(G&&) // moving once ...
G& G::operator=(G&&) // moving twice ... (not really, because that is not implemented!)
c1
mem_G:
mem_UG: I am UG
mem_T: I am T
mem_FT: I am FT
c2
mem_G: I am G // this memory hasn't been moved because G::operator(G&&)
mem_UG: // does not implement the move.
mem_T:
mem_FT:
Notice how mem_G in its last appearance kept its value in c2. In case I defaulted G& operator=(G&&) instead of defining it, the result only differs in that line:
c2
mem_G: // this memory has been moved twice
Question How do I implement the move assignment operators (and the move constructors, in case it is needed) within this inheritance structure so that both move the memory only once? Is it possible to have such code without the warning above?
Thanks in advance.
Edit This problem has been solved thanks to this answer. I thought people would find it useful to see a complete proposal of a solution, so I'm adding an extended version of the MWE with two more classes so that it is a little bit more complicated. Also, there is the main function so the classes can be tested. Finally, I would like to add that valgrind does not complain about memory leaks when executing a debug compilation of the code.
Edit I completed the example following the rule of 5, just like one of the users who commented on this answer pointed out, and I thought I would update the answer. The code compiles with no warning with the flags -Wall -Wpedantic -Wshadow -Wextra -Wconversion -Wold-style-cast -Wrestrict -Wduplicated-cond -Wnon-virtual-dtor -Woverloaded-virtual and the execution with valgrind does not produce any errors. I've also added couts with the __PRETTY_FUNCTION__ macro so that anyone who wishes to test the code can see the trace of function calls.
#include <functional>
#include <iostream>
#include <string>
using namespace std;
class G {
public:
G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = "empty";
}
G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
}
G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
}
virtual ~G() { }
G& operator= (const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(g);
return *this;
}
G& operator= (G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_G(std::move(static_cast<G&>(g)));
return *this;
}
friend ostream& operator<< (ostream& os, const G& r) {
os << " mem_G: " << r.mem_G;
return os;
}
virtual void asdf() = 0;
string mem_G;
protected:
void copy_full_G(const G& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = g.mem_G;
}
void move_full_G(G&& g) {
cout << __PRETTY_FUNCTION__ << endl;
mem_G = std::move(g.mem_G);
}
};
class UG : virtual public G {
public:
UG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_UG = "empty";
}
UG(const UG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
}
UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
}
virtual ~UG() { }
UG& operator= (const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(u);
return *this;
}
UG& operator= (UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_UG(std::move(static_cast<UG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const UG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_UG;
protected:
void copy_full_UG(const UG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_UG = u.mem_UG;
}
void move_full_UG(UG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_UG = std::move(u.mem_UG);
}
};
class DG : virtual public G {
public:
DG() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_DG = "empty";
}
DG(const DG& u) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
}
DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
}
virtual ~DG() { }
DG& operator= (const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(u);
return *this;
}
DG& operator= (DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_DG(std::move(static_cast<DG&>(u)));
return *this;
}
friend ostream& operator<< (ostream& os, const DG& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG;
return os;
}
void asdf() { mem_G = "asdf"; }
string mem_DG;
protected:
void copy_full_DG(const DG& u) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_G(u);
mem_DG = u.mem_DG;
}
void move_full_DG(DG&& u) {
cout << __PRETTY_FUNCTION__ << endl;
// move parent class
move_full_G(std::move(static_cast<G&>(u)));
// move this class' members
mem_DG = std::move(u.mem_DG);
}
};
class T : virtual public G {
public:
T() : G() {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = "empty";
}
T(const T& t) : G() {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
}
T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
}
virtual ~T() { }
T& operator= (const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
copy_only_T(t);
return *this;
}
T& operator= (T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
move_only_T(std::move(static_cast<T&>(t)));
return *this;
}
friend ostream& operator<< (ostream& os, const T& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_T: " << r.mem_T;
return os;
}
virtual void qwer() = 0;
string mem_T;
protected:
// Copy *only* T members.
void copy_only_T(const T& t) {
cout << __PRETTY_FUNCTION__ << endl;
mem_T = t.mem_T;
}
// Move *only* T members.
void move_only_T(T&& t) {
cout << __PRETTY_FUNCTION__ << endl;
// if we moved G's members too then we
// would be moving G's members twice!
//move_full_G(std::move(static_cast<G&>(t)));
mem_T = std::move(t.mem_T);
}
};
class FT : public UG, virtual public T {
public:
FT() : T(), UG(){
cout << __PRETTY_FUNCTION__ << endl;
mem_FT = "empty";
}
FT(const FT& f) : G(), T(), UG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
}
FT(FT&& f) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_FT(std::move(static_cast<FT&>(f)));
}
virtual ~FT() { }
FT& operator= (const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_FT(f);
return *this;
}
FT& operator= (FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign FT members
move_full_FT(std::move(static_cast<FT&>(other)));
return *this;
}
friend ostream& operator<< (ostream& os, const FT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_UG: " << r.mem_UG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_FT: " << r.mem_FT;
return os;
}
void qwer() { mem_FT = "zxvc"; }
string mem_FT;
protected:
void copy_full_FT(const FT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_UG(f);
copy_only_T(f);
mem_FT = f.mem_FT;
}
void move_full_FT(FT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign UG members and also the base class's members
move_full_UG(std::move(static_cast<UG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_FT = std::move(other.mem_FT);
}
};
class RT : public DG, virtual public T {
public:
RT() : T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
mem_RT = "empty";
}
RT(const RT& f) : G(), T(), DG() {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(f);
}
RT(RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
move_full_RT(std::move(static_cast<RT&>(r)));
}
virtual ~RT() { }
RT& operator= (const RT& r) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_RT(r);
return *this;
}
RT& operator= (RT&& r) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign RT members
move_full_RT(std::move(static_cast<RT&>(r)));
return *this;
}
friend ostream& operator<< (ostream& os, const RT& r) {
os << " mem_G: " << r.mem_G << endl;
os << " mem_DG: " << r.mem_DG << endl;
os << " mem_T: " << r.mem_T << endl;
os << " mem_RT: " << r.mem_RT;
return os;
}
void qwer() { mem_RT = "zxvc"; }
string mem_RT;
protected:
void copy_full_RT(const RT& f) {
cout << __PRETTY_FUNCTION__ << endl;
copy_full_DG(f);
copy_only_T(f);
mem_RT = f.mem_RT;
}
void move_full_RT(RT&& other) {
cout << __PRETTY_FUNCTION__ << endl;
// Move-assign DG members and also the base class's members
move_full_DG(std::move(static_cast<DG&>(other)));
// Move-assign only T's members
move_only_T(std::move(static_cast<T&>(other)));
// move this class' members
mem_RT = std::move(other.mem_RT);
}
};
template<class C> void test_move(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
init_C(c1);
cout << "Initialise c1" << endl;
cout << c1 << endl;
cout << "Move constructor: 'c2 <- c1'" << endl;
C c2 = std::move(c1);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Move assignment operator: 'c1 <- c2'" << endl;
c1 = std::move(c2);
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C> void test_copy(const function<void (C&)>& init_C) {
C c1;
cout << c1 << endl;
cout << "Initialise c1" << endl;
init_C(c1);
cout << c1 << endl;
cout << "Copy constructor: 'c2 <- c1'" << endl;
C c2 = c1;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
cout << "Copy assignment operator: 'c1 <- c2'" << endl;
c1 = c2;
cout << "c1" << endl;
cout << c1 << endl;
cout << "c2" << endl;
cout << c2 << endl;
}
template<class C>
void test(const string& what, const function<void (C&)>& init_C) {
cout << "********" << endl;
cout << "** " << what << " **" << endl;
cout << "********" << endl;
cout << "----------" << endl;
cout << "-- MOVE --" << endl;
cout << "----------" << endl;
test_move<C>(init_C);
cout << "----------" << endl;
cout << "-- COPY --" << endl;
cout << "----------" << endl;
test_copy<C>(init_C);
}
int main() {
test<UG>(
"UG",
[](UG& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
}
);
test<DG>(
"DG",
[](DG& d) -> void {
d.mem_G = "I am G";
d.mem_DG = "I am DG";
}
);
test<FT>(
"FT",
[](FT& u) -> void {
u.mem_G = "I am G";
u.mem_UG = "I am UG";
u.mem_T = "I am T";
u.mem_FT = "I am FT";
}
);
test<RT>(
"RT",
[](RT& u) -> void {
u.mem_G = "I am G";
u.mem_DG = "I am DG";
u.mem_T = "I am T";
u.mem_RT = "I am RT";
}
);
}
The problem is that FT's FT& operator= (FT&&) = default; is essentially:
FT& operator=(FT&& other) {
// Move-assign base classes
static_cast<UG&>(*this) = std::move(static_cast<UG&>(other)); // Also move-assigns G
// other.mem_G is now empty after being moved
static_cast<T&>(*this) = std::move(static_cast<T&>(other)); // Also move-assigns G
// this->mem_G is now empty
// Move-assign members
mem_FT = std::move(other.mem_FT);
}
(Though not exactly. A compiler is allowed to be smart and only move from a virtual base class once, but that doesn't happen with gcc and clang at least)
Where the single base class subobject G is moved into from other twice (through the two move assigns). But other.mem_G is empty after the first move, so it will be empty after the move assign.
The way to deal with this is to make sure that the virtual base is only move-assigned once. This can easily be done by writing something like this:
FT& operator=(FT&& other) noexcept {
// Also move-assigns `G`
static_cast<T&>(*this) = std::move(static_cast<T&>(other));
// Move-assign UG members without UG's move assign that moves `G`
mem_UG = std::move(other.mem_UG);
// Move-assign FT members
mem_FT = std::move(other.mem_FT);
}
With private members or a more complicated move-assign, you might want to make a protected move_only_my_members_from_this_type_and_not_virtual_bases(UG&&) member function
You can also fix this by not generating a default move-assign operator, making the base class be copied twice instead of becoming empty, for a potential performance hit.
I have an explicit function that takes a reference to the base type of a class. What is the proper way to pass that in?
I am currently doing a static cast:
#include <iostream>
using namespace std;
struct Base
{
Base() { cout << "Base Constructor" << endl; }
Base(Base const& c) { cout << "Base-Base Constructor" << endl; }
};
struct Derived : public Base
{
Derived() { cout << "Derived Constructor" << endl; }
explicit Derived(Base const& c) { cout << "Derived-Base Constructor" << endl; }
Derived(Derived const& c) { cout << "Derived-Derived Constructor" << endl; }
};
int main()
{
Base B;
cout << "\n";
Derived D;
cout << "\n";
Base* test1 = new Derived(D);
cout << "\n";
Base* test3 = new Derived(static_cast<Base>(D));
cout << "\n";
Base* test2 = new Derived(B);
cout << "\n";
return 0;
}
but that calls the copy constructor of the base class.
I could pass *static_cast<Base*>(&D), but that seems a bit hackish. I feel like I am just overlooking a simple way to do this. Thanks.
Use this:
static_cast<Base&>(D)
Or this:
static_cast<const Base&>(D)
Consider this piece of code and its output:
class test {
int a, b;
public:
test (int a, int b) : a(a), b(b) { cout << "Const" << endl;}
test (const test & t) {
cout << "Copy constructor" << endl;
cout << "Being copied to = " << this << " from " << &t << endl;
if (&t == this) {
cout << "returning" << endl;
return;
}
this->a = t.a;
this->b = 6;//t.b;
}
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
}
test get () {
test l = test (3, 3);
cout << "Local return " << &l << endl;
return l;
}
void display () {
cout << a << " " << b << endl;
}
};
int main () {
int i = 5, &ref = i;
test t(1,1), u(2,2);
u = t.get();
cout << "u address" << &u <<endl;
//cout << "u ka = " << &u << endl;
u.display();
}
Output:
Const
Const
Const
Local return 0x7fff680e5ab0
Assignment operator
Being written to = 0x7fff680e5a90 from 0x7fff680e5ab0
Copy constructor
Being copied to = 0x7fff680e5a80 from 0x7fff680e5a90
u address0x7fff680e5a90
3 3
I know the way to return from an assignment is by reference, but I was trying to understand how this works since I am a beginner to C++.
What is the address 0x7fff680e5a80 ? Where is this coming from ? Which object is calling the copy constructor here ? I would expect u (address 0x7fff680e5a90) to call it.
Lets follow your code (I modified your constructor to dump the constructed this).
First you isntantiate t and u.
Const at 0x7fffffffdeb0
Const at 0x7fffffffdea0
Then you call t.get, which initializes l.
Const at 0x7fffffffdec0
You then return l.
Local return 0x7fffffffdec0
The copy constructor that would normally happen at this point is elided.
Assignment operator from l to u
Being written to = 0x7fffffffdea0 from 0x7fffffffdec0
Then you are returning the assignment by value (you should return it by reference), so you copy from u to an output value that isn't stored (0x7fffffffde90 from u)
Copy constructor
Being copied to = 0x7fffffffde90 from 0x7fffffffdea0
And then you print u.
u address0x7fffffffdea0
Inside T.
To get rid of the confusing (and unessisary copy) you should return by reference when you do any assignment operator:
test& operator=(const test & in);
Your assignment operator is incorrect:
test operator=(const test & in) {
You should return by reference:
test &operator=(const test & in) {
The object at 0x7fff680e5a80 is a prvalue temporary object of type test that is returned from your (incorrect) assignment operator and immediately discarded:
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
} // ^-- copy constructor is called here
You can check this by taking an rvalue reference to the return value of the assignment operator:
auto &&temp = (u = t.get());
std::cout << "Address of temp: " << &temp << std::endl; // prints 0x7fffffffde90
There is such code:
#include <iostream>
class A {
public:
int a;
A() : a(0) {
std::cout << "Default constructor" << " " << this << std::endl;
}
A(int a_) : a(a_) {
std::cout << "Constructor with param " << a_ << " " << this << std::endl;
}
A(const A& b) {
a = b.a;
std::cout << "Copy constructor " << b.a << " to " << a << " " << &b << " -> " << this << std::endl;
}
A& operator=(const A& b) {
a=b.a;
std::cout << "Assignment operator " << b.a << " to " << a << " " << &b << " -> " << this << std::endl;
}
~A() {
std::cout << "Destructor for " << a << " " << this << std::endl;
}
void show(){
std::cout << "This is: " << this << std::endl;
}
};
A fun(){
A temp(3);
temp.show();
return temp;
}
int main() {
{
A ob = fun();
ob.show();
}
return 0;
}
Result:
Constructor with param 3 0xbfee79dc
This is: 0xbfee79dc
This is: 0xbfee79dc
Destructor for 3 0xbfee79dc
Object ob is initialized by function fun(). Why copy constructor is not called there? I thought that when function returns by value then copy constructor or assignment operator is called. It seems that object constructed in function fun() is not destroyed after execution of function. How can be copy constructor forced to invoke in this case?
This was compiled by g++.
Why copy constructor is not called there?
RVO
How can be copy constructor forced to invoke in this case?
Pass an option to the compiler. For gcc, it is --no-elide-constructors option to disable the RVO
That is called Named Return Value Optimization and copy elision, and basically means that the compiler has figured out that the copy can be avoided by carefully placing the temporary and the object in the same memory location.
By default there would be three objects in that piece of code, temp inside fun, the return value and ob inside main, and as many as two copies, but by carefully placing temp in the same memory location as the returned object inside fun and placing ob in the same memory address the two copies can be optimized away.
I wrote about those two optimizations with a couple of pictures to explain what is going on here:
NRVO
Copy elision