Below is my code:
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle(int, int);
int area() { return (width * height); }
};
Rectangle::Rectangle(int a, int b) {
width = a;
height = b;
}
int main() {
Rectangle A(3, 4);
Rectangle B = Rectange(3,4);
return 0;
}
I didn't define any copy constructors or assignment operator for Rectangle
class.
Is it true that Rectangle B = Rectangle(3, 4);
actually does three things in serial?
Allocate memory space for a temporary variable (let's use tmp
to denote it) of Rectangle, call Rectangle::Rectangle(3, 4)
to intialize it.
Allocate memory space for variable B
, initialize it with the default constructor
(memberwise) Copy tmp
to B
with the assignment operator Rectangle& operator = (const Rectangle &)
Does that explanation make sense? I think I might understand wrongly because this process looks very clumsy and inefficient compared with Rectangle A(3, 4);
.
Does anyone have ideas about this? Is it true that Rectangle A(3,4)
is equivalent to Rectangle A = Rectangle(3, 4);
? Thanks!
Rectangle A(3, 4);
always simply invokes the Rectangle(int, int)
constructor, throughout all of the history of C++ having constructors. Simple. Boring.
Now for the fun part.
In the newest (as of this writing) edition of the standard, Rectangle B = Rectangle(3,4);
immediately collapses down into Rectangle B(3,4);
This happens regardless of what the nature of Rectangle
's move or copy constructors is. This feature is commonly referred to as guaranteed copy elision, although it's important to stress that there is no copy and no move here. What happens is B
is direct-initialized from (3,4)
.
Before C++17, there is a temporary Rectangle
constructed that a compiler may optimize away (and by may, I mean it will certainly, unless you tell it not to). But your ordering of events is not correct. It's important to note that no assignment happens here. We are not assigning to B
. We are constructing B
. Code of the form:
T var = expr;
is copy-initialization, it is not copy-assignment. Hence, we do two things:
Rectangle
using the Rectangle(int, int)
constructorRectangle
constructor given a prvalue of type Rectangle
)If the move constructor is deleted (or, pre-C++11, the copy constructor is marked private
), then trying to construct B
in this way is ill-formed. If the special member functions are left alone (as they are in this example), the two declarations of A
of B
will certainly compile to the same code.
The form of initialization in B
may look more familiar if you actually drop the type:
auto B = Rectangle(3,4);
This is the so-called AAA (Almost Always Auto) style of declarations that Herb Sutter is a fan of. This does exactly the same thing as Rectangle B = Rectangle(3, 4)
, just with the leading step of first deducing the type of B
as Rectangle
.