rvalue as initialiser to construct an object - c++

I am new to programming. sorry for my bad english.
I have tried to use rvalue as initialiser to initial objects.
So, according to the code, it would print out what are the used constructor and assignment operator.
But turned out object "what2" and "what3", those don't print out anything.
here is the code:
#include <iostream>
using namespace std;
class X{
public:
int x;
X()= default;
X(int num):x{num}{}
X(const X& y){
x = y.x;
std::cout << "copy constructor" << std::endl;
std::cout << x << std::endl;
}
X& operator=(const X& d){
x = d.x;
std::cout << "copy assignment" << std::endl;
return *this;
}
X(X&& y){
x = y.x;
std::cout << "move constructor" << std::endl;
}
X& operator=(X&& b){
x = b.x;
std::cout << "move assignment" << std::endl;
return *this;
}
};
X operator +(const X& a,const X& b){
X tmp{};
tmp.x = a.x +b.x;
return tmp;
}
int main(int argc, const char * argv[]) {
X a{7} , b{11};
X what1;
cout << "---------------" << endl;
what1 = a+b;
cout << "---------------" << endl;
X what2{a+b};
cout << "---------------" << endl;
X what3 = a+b;
cout << "---------------" << endl;
std::cout << what1.x << std::endl;
std::cout << what2.x << std:: endl;
std::cout <<what3.x << std::endl;
return 0;
}
the output is:
---------------
move assignment
---------------
---------------
---------------
18
18
18
Program ended with exit code: 0
only "what1" uses assignment properly.
so, how can i use rvalue to initial an object? and using operator= to initial an object?
thank you very much.

Your code could result in move operations being used, but your compiler has chosen to elide those moves and allocate the return of operator+ directly at the call site. You can see this happening if you disable copy elision in your compiler (-fno-elide-constructors in GCC or Clang).
Your move constructor and assignment operator will be successfully used in contexts in which copy elision is not permitted, such as this:
X what2 { std::move(what1) }; //can't elide copy, move constructor used

The following code triggers more of your constructors/operators, check it out to see which trigger in what cases
#include <iostream>
using namespace std;
class X{
public:
int x;
X()
{
x = 0;
std::cout << "constructor" << std::endl;
}
X(int num):x{num}
{
std::cout << "list initilizer" << std::endl;
}
X(const X& y){
x = y.x;
std::cout << "copy constructor" << std::endl;
}
X& operator=(const X& d){
x = d.x;
std::cout << "copy assignment" << std::endl;
return *this;
}
X(X&& y){
x = y.x;
std::cout << "move constructor" << std::endl;
}
X& operator=(X&& b){
x = b.x;
std::cout << "move assignment" << std::endl;
return *this;
}
};
X operator +(const X& a,const X& b){
X tmp{};
tmp.x = a.x +b.x;
return tmp;
}
int main(int argc, const char * argv[]) {
X a{7} , b{11};
cout << "---------------" << endl;
X what1;
cout << "---------------" << endl;
what1 = a+b;
cout << "---------------" << endl;
X what2(a+b);
cout << "---------------" << endl;
X what3 = X(a);
cout << "---------------" << endl;
X what4 = X();
cout << "---------------" << endl;
X what5 = std::move(what1);
cout << "---------------" << endl;
what5 = std::move(what1);
cout << "---------------" << endl;
what5 = what1;
cout << "---------------" << endl;
return 0;
}

GCC provides the -fno-elide-constructors option to disable copy-elision. if you want to elide the copy-elision then use flag -fno-elide-constructors. Refer https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization/27916892#27916892 for more detail

Related

Move assignment operator and virtual inheritance

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.

C++ copy on member access

I was doing some experiments to see when copy is performed apart from copy elision, RVO, NRVO cases.
So I've written some code like this:
class X {
public:
X() { std::cout << "Default constructor" << std::endl; }
X(const X&) { std::cout << "Copy constructor" << std::endl; }
X(X&&) { std::cout << "Move constructor" << std::endl; }
X& operator=(const X) {
std::cout << "Assignment operator" << std::endl;
return *this;
}
X& operator=(X&&) {
std::cout << "Move assignment operator" << std::endl;
return *this;
}
~X() { std::cout << "Destructor" << std::endl; }
};
class Y {
private:
X x;
public:
const X& getX() const {
std::cout << "getX" << std::endl;
return x;
}
};
int main() {
Y y;
std::cout << "assign to ref" << std::endl;
const X& x1 = y.getX();
(void)x1;
std::cout << "assign to const" << std::endl;
const X x2 = y.getX();
return 0;
}
and I receive the following as output:
Default constructor
assign to ref
getX
assign to const
getX
Copy constructor
Destructor
Destructor
Both when compiled with gcc or clang with -O3 and tried -std=c++{11,14,17} all produced the same output.
Which surprised me was, I wasn't expecting any copy to be performed when using y.getX(); to a const variable. It is something I used frequently just to ease my access to that variable and its members in the following code, but I wasn't doing it over a const reference instead I was just using const hoping the compiler would regard it just as a renaming.
Does anyone knows why exactly is that copy performed? Only reason that comes to my mind is that it is to make code thread-safe. If there are multiple threads working with object y, then my assignment to const would not be that const after all. Since it would just reference the member x in object y. Which might be changed by other threads. But I am not sure whether that's the real intention or not.
To see the effect of RVO verses compiler forced use of NRVO, play with -fno-elide-constructors compiler switch on the following modified program below. With the usual options you get:
Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
make_X copy
Default constructor 3
make_X (with id: 3)
x3 (id:3)
make_X ref
Default constructor 4
make_X (with id: 4)
x4 (id:4)
Destructor 4
Destructor 3
Destructor 2
Destructor 1
But with NRVO you get:
Default constructor 1
assign to ref
getX (with id: 1)
x1 (id:1)
assign to const
getX (with id: 1)
Copy constructor 2
x2 (id:2)
additional 1
Default constructor 3
make_X (with id: 3)
Move constructor 4
Destructor 3
Move constructor 5
Destructor 4
x3 (id:5)
additional 2
Default constructor 6
make_X (with id: 6)
Move constructor 7
Destructor 6
x4 (id:7)
Destructor 7
Destructor 5
Destructor 2
Destructor 1
Code example:
#include <iostream>
int global_id;
class X {
public:
X() : id(++global_id) {
std::cout << "Default constructor " << id << std::endl;
}
X(const X&) : id(++global_id) {
std::cout << "Copy constructor " << id << std::endl;
}
X(X&&) : id(++global_id) {
std::cout << "Move constructor " << id << std::endl;
}
X& operator=(const X&) {
std::cout << "Assignment operator " << id << std::endl;
return *this;
}
X& operator=(X&&) {
std::cout << "Move assignment operator " << id << std::endl;
return *this;
}
~X() {
std::cout << "Destructor " << id << std::endl;
}
int id;
};
class Y {
X x;
public:
const X& getX() const {
std::cout << "getX (with id: " << x.id << ')' << std::endl;
return x;
}
X make_X() const {
X extra;
std::cout << "make_X (with id: " << extra.id << ')' << std::endl;
return extra;
}
};
int main()
{
Y y;
std::cout << "assign to ref" << std::endl;
const X& x1 = y.getX();
std::cout << "x1 (id:" << x1.id << ")\n";
(void) x1;
std::cout << "assign to const" << std::endl;
const X x2 = y.getX();
std::cout << "x2 (id:" << x2.id << ")\n";
std::cout << "make_X copy" << std::endl;
const X x3 = y.make_X();
std::cout << "x3 (id:" << x3.id << ")\n";
std::cout << "make_X ref" << std::endl;
const X& x4 = y.make_X();
std::cout << "x4 (id:" << x4.id << ")\n";
return 0;
}
As you see, the RVO really only comes to play with local variables.

When I return by value from an assignment operator, firstly what is the mechanism and basis of calling the copy constructor?

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

ambiguous overload for ‘operator=’ with c++11 std::move and copy and swap idiom

I am getting the following error:
[matt ~] g++ -std=c++11 main.cpp -DCOPY_AND_SWAP && ./a.out
main.cpp: In function ‘int main(int, const char* const*)’:
main.cpp:101:24: error: ambiguous overload for ‘operator=’ in ‘move = std::move<Test&>((* & copy))’
main.cpp:101:24: note: candidates are:
main.cpp:39:7: note: Test& Test::operator=(Test)
main.cpp:52:7: note: Test& Test::operator=(Test&&)
When the following code is compiled:
#include <iostream>
#include <unordered_map>
class Test final {
public:
typedef std::unordered_map<std::string, std::string> Map;
public:
Test();
explicit Test(Map&& map);
~Test();
Test(const Test& other);
Test(Test&& test);
#ifdef COPY_AND_SWAP
Test& operator=(Test other);
#else
Test& operator=(const Test& other);
#endif
Test& operator=(Test&& other);
size_t Size() const noexcept;
friend void swap(Test& lhs, Test& rhs);
private:
friend std::ostream& operator<<(std::ostream& stream, const Test& test);
private:
Map map_;
};
Test::Test() : map_() {
std::cerr << "Default constructor called" << std::endl;
};
Test::Test(const Test& other) : map_(other.map_) {
std::cerr << "Copy constructor called" << std::endl;
};
Test::Test(Test&& other) : map_(std::move(other.map_)) {
std::cerr << "Move constructor called" << std::endl;
};
Test::Test(Map&& map) : map_(std::move(map)) {
std::cerr << "Map constructor called" << std::endl;
};
Test::~Test() {};
#ifdef COPY_AND_SWAP
Test& Test::operator=(Test other) {
std::cerr << "Copy and swap assignment called" << std::endl;
using std::swap;
swap(this->map_, other.map_);
return *this;
}
#else
Test& Test::operator=(const Test& other) {
std::cerr << "Copy assignment called" << std::endl;
this->map_ = other.map_;
return *this;
}
#endif
Test& Test::operator=(Test&& other) {
std::cerr << "Move assignment called" << std::endl;
this->map_ = other.map_;
other.map_.clear();
return *this;
}
size_t Test::Size() const noexcept {
return map_.size();
}
void swap(Test& lhs, Test& rhs) {
using std::swap;
swap(lhs.map_, rhs.map_);
}
std::ostream& operator<<(std::ostream& stream, const Test& test) {
return stream << test.map_.size();
}
int main (const int argc, const char * const * const argv) {
using std::swap;
Test::Map map {
{"some", "dummy"},
{"data", "to"},
{"fill", "up"},
{"the", "map"}
};
std::cout << " map size(): " << map.size() << std::endl;
std::cout << "Constructing" << std::endl;
Test test(std::move(map));
std::cout << " map.size(): " << map.size() << std::endl;
std::cout << "test.Size(): " << test.Size() << std::endl;
std::cout << "Copy construction" << std::endl;
Test copy(test);
std::cout << "copy.Size(): " << copy.Size() << std::endl;
std::cout << "Move construction" << std::endl;
Test move(std::move(copy));
std::cout << "move.Size(): " << move.Size() << std::endl;
std::cout << "copy.Size(): " << copy.Size() << std::endl;
std::cout << "Swapping" << std::endl;
swap(move, copy);
std::cout << "move.Size(): " << move.Size() << std::endl;
std::cout << "copy.Size(): " << copy.Size() << std::endl;
std::cout << "Swapping back" << std::endl;
swap(move, copy);
std::cout << "move.Size(): " << move.Size() << std::endl;
std::cout << "copy.Size(): " << copy.Size() << std::endl;
std::cout << "Copy assignment" << std::endl;
copy = test;
std::cout << "test.Size(): " << test.Size() << std::endl;
std::cout << "copy.Size(): " << copy.Size() << std::endl;
std::cout << "Move assignment" << std::endl;
move = std::move(copy);
std::cout << "move.Size(): " << move.Size() << std::endl;
std::cout << "copy.Size(): " << copy.Size() << std::endl;
return 0;
}
When compiled with g++ -std=c++11 main.cpp && ./a.out:
[matt ~] g++ -std=c++11 main.cpp && ./a.out
map size(): 4
Constructing
Map constructor called
map.size(): 0
test.Size(): 4
Copy construction
Copy constructor called
copy.Size(): 4
Move construction
Move constructor called
move.Size(): 4
copy.Size(): 0
Swapping
move.Size(): 0
copy.Size(): 4
Swapping back
move.Size(): 4
copy.Size(): 0
Copy assignment
Copy assignment called
test.Size(): 4
copy.Size(): 4
Move assignment
Move assignment called
move.Size(): 4
copy.Size(): 0
Could someone help me understand why the ambiguity occurs when using the copy and swap idiom in this case?
for overload resolution purposes the functions
Test& operator=(Test other);
Test& operator=(Test&& other);
are equal, because the implicit conversion sequences used to convert to Test and Test&&, respectively, are equal. The former is not better, because the direct reference binding is also considered an identity conversion.
When confronted with the ambiguity of 2 equally good matches, the compiler gives an error. You probably want this:
#ifdef COPY_AND_SWAP
Test& operator=(Test other);
#else
Test& operator=(const Test& other);
Test& operator=(Test&& other);
#endif

Why my copy constructor not called? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What are copy elision and return value optimization?
I have the following program:
#include <iostream>
using namespace std;
class Pointt {
public:
int x;
int y;
Pointt() {
x = 0;
y = 0;
cout << "def constructor called" << endl;
}
Pointt(int x, int y) {
this->x = x;
this->y = y;
cout << "constructor called" << endl;
}
Pointt(const Pointt& p) {
this->x = p.x;
this->y = p.y;
cout << "copy const called" << endl;
}
Pointt& operator=(const Pointt& p) {
this->x = p.x;
this->y = p.y;
cout << "op= called" << endl;
return *this;
}
};
Pointt func() {
cout << "func: 1" << endl;
Pointt p(1,2);
cout << "func: 2" << endl;
return p;
}
int main() {
cout << "main:1" << endl;
Pointt k = func();
cout << "main:2" << endl;
cout << k.x << " " << k.y << endl;
return 0;
}
The output I expect is the following:
main:1
func: 1
constructor called
func: 2
copy const called
op= called
main:2
1 2
But I get the following:
main:1
func: 1
constructor called
func: 2
main:2
1 2
The question is: why doesn't returning an object from func to main call my copy constructor?
This is due to Return Value Optimization. This is one of the few instances where C++ is allowed to change program behavior for an optimization.