I was studying move constructors when I came across this sample of code on official Microsoft C++ documentation which was trying to explain basic move constructor.
Link to article:- https://learn.microsoft.com/en-us/cpp/cpp/constructors-cpp?view=msvc-170#move_constructors
Context :-
The compiler chooses a move constructor in certain situations where the object is being initialized by another object of the same type that is about to be destroyed and no longer needs its resources. The following example shows one case when a move constructor is selected by overload resolution. In the constructor that calls get_Box(), the returned value is an xvalue (eXpiring value). It is not assigned to any variable and is therefore about to go out of scope. To provide motivation for this example, let's give Box a large vector of strings that represent its contents. Rather than copying the vector and its strings, the move constructor "steals" it from the expiring value "box" so that the vector now belongs to the new object. The call to std::move is all that's needed because both vector and string classes implement their own move constructors.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Box {
public:
Box() { std::cout << "default" << std::endl; }
Box(int width, int height, int length)
: m_width(width), m_height(height), m_length(length)
{
std::cout << "int,int,int" << std::endl;
}
Box(Box& other)
: m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
{
std::cout << "copy" << std::endl;
}
Box(Box&& other) : m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
{
m_contents = std::move(other.m_contents);
std::cout << "move" << std::endl; // HERE
}
int Volume() { return m_width * m_height * m_length; }
void Add_Item(string item) { m_contents.push_back(item); }
void Print_Contents()
{
for (const auto& item : m_contents)
{
cout << item << " ";
}
}
private:
int m_width{ 0 };
int m_height{ 0 };
int m_length{ 0 };
vector<string> m_contents;
};
Box get_Box()
{
Box b(5, 10, 18); // "int,int,int"
b.Add_Item("Toupee");
b.Add_Item("Megaphone");
b.Add_Item("Suit");
return b;
}
int main()
{
Box b; // "default"
Box b1(b); // "copy"
Box b2(get_Box()); // "move" //HERE
cout << "b2 contents: ";
b2.Print_Contents(); // Prove that we have all the values
char ch;
cin >> ch; // keep window open
return 0;
}
In the main() function when calling Box b2(get_Box()); // "move"
, wasn't the compiler meant to call the move constructor defined in the class definition Box(Box&& other)
?
BUT when I run the code on my machine I receive the following output :
default
copy
int,int,int
b2 contents: Toupee Megaphone Suit
Why is "move" not being printed? std::cout << "move" << std::endl;
Is the move constructor defined in the class not being called? If not then what's happening ? Which constructor is doing the job?
Also when I execute Box b2(std::move(get_Box())); // "move"
instead of Box b2(get_Box()); // "move"
, I get the following output
default
copy
int,int,int
move
b2 contents: Toupee Megaphone Suit
One can clearly see that this time "move" std::cout << "move" << std::endl;
gets printed. If someone could explain in detail what's going on?