Here is a code sample from a C++ quiz:
#include <iostream>
struct X {
X(const char *) { std::cout << 1; }
X(const X &) { std::cout << 2; }
X(X &&) { std::cout << 3; }
};
X f(X a) {
return a;
}
X g(const char * b) {
X c(b);
return c;
}
int main() {
f("hello");
g("hello");
}
What will be the output of the program?
I think this way:
f(X a)
is called, and the constructor implicitly converts const char*
to X, so the output is 1
g(const char*)
is called, and X c(b)
X(const char*)
The output is 1
So the answer is 11. The answer which is given to the quiz is 131. The answer, which I get with g++ 4.4.4-13 is 121.
It is said that this code was compiled with this command:
g++ -std=c++11 -Wall -Wextra -O -pthread
Where does the middle number come from? And why can it be 3 or 2?
Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.
In the C++ programming language, the move assignment operator = is used for transferring a temporary object to an existing object. The move assignment operator, like most C++ operators, can be overloaded. Like the copy assignment operator it is a special member function.
noexcept is nice for two reasons: The compiler can optimize a little better because it doesn't need to emit any code for unwinding a call stack in case of an exception, and. It leads to incredible performance differences at runtime for std::vector (and other containers, too)
The move constructor and move assignment operator are simple. Instead of deep copying the source object (a) into the implicit object, we simply move (steal) the source object's resources. This involves shallow copying the source pointer into the implicit object, then setting the source pointer to null.
In theory, this can print any of 131
, 13313
, 1313
, and 1331
. It's pretty silly as a quiz question.
f("hello");
:
X
via the converting constructor, prints 1
.X
is used to initialize the function argument, calls the move constructor, prints 3
. This can be elided.x
is used to initialize the temporary return value, calls the move constructor, prints 3
. It's a function parameter, so no elision is allowed, but the return is an implicit move.g("hello");
c
via the converting constructor, prints 1
.c
is used to initialize the temporary return value, calls the move constructor, prints 3
. This can be elided.Remember that functions always have to construct the thing they return, even if it's just discarded by the calling code.
As to printing 2
, that's because the ancient compiler you are using doesn't implement the implicit-move-when-returning-a-local-variable rule.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With