This question already has answers here:
Why copy constructor is not called in this case?
(4 answers)
Copy constructor not called?
(2 answers)
Closed 9 years ago.
When returning object, I thought returning pointer or reference is the right way because temporary object is created when returning an object.
In this example, hello() function returns an object of type A, and the returned value is stored in object a.
A hello()
{
A a(20); // <-- A object is created in stack
cout << &a << endl;
return a; // <-- Temporary object is created for return value
}
void test1()
{
A a = hello(); // copy constructor is called
cout << &a << endl;
}
My expectation was there should three constructor called. However, when I executed test1() with g++ 4.8, the constructor is called only once, not trice.
class A
{
int x;
public:
A() {
cout << "Default constructor called" << endl;
}
A(const A& rhs)
{
this->x = rhs.x;
cout << "Copy constructor called" << endl;
}
A& operator=(const A& rhs)
{
this->x = rhs.x;
cout << "Copy constructor called" << endl;
}
A(int x) : x(x) {
cout << "*I'm in" << endl;
}
~A() {
cout << "*I'm out" << endl;
}
int get() const {return x;}
void set(int x) {this->x = x;}
};
Execution result:
*I'm in
0x7fff62bb30c0 <-- from hello()
0x7fff62bb30c0 <-- from test1()
*I'm out
Is this expected behavior in C++ or did I get this result because of g++ optimization?
The A a(20) object is generated in the stack of the hello() function, and it is supposed to be deleted on return. How the object stack can be not deleted and passed to the caller?
Related
This question already has answers here:
Why is using the move constructor in a return statement legal?
(2 answers)
C++11 rvalues and move semantics confusion (return statement)
(6 answers)
move constructor called on return instead of copy
(1 answer)
Closed 4 months ago.
#include <iostream>
#include <stdio.h>
class Stack{
public:
Stack(int size):msize(size){
std::cout << "Stack::Stack() called" << std::endl;
}
// Constructor 2: Copy constructor with non-const lvalue reference
Stack(Stack &src)
:msize(src.msize)
{
std::cout << "Stack::Stack(const Stack&)" << std::endl;
}
// Constrcutor 3: Move constructor with rvalue reference
Stack(Stack &&src)
:msize(src.msize)
{
std::cout << "Stack::Stack(Stack&&)" << std::endl;
}
~Stack()
{
std::cout << "~Stack()" << std::endl;
}
Stack& operator=(const Stack &src)
{
std::cout << "operator=" << std::endl;
if (this == &src)
return *this;
return *this;
}
Stack& operator=(Stack &&src)
{
std::cout << "operator=&&" << std::endl;
if (this == &src)
return *this;
return *this;
}
int getSize()
{
return msize;
}
private:
int msize;
};
Stack GetStack(Stack &stack)
{
Stack tmp(stack.getSize());
std::cout << "tmp is created" << std::endl;
return tmp;
};
int main(){
Stack s(100);
std::cout << "s is created" << std::endl;
s = GetStack(s);
return 0;
}
I define 3 constructors in Stack class. If I run the main function without any change, then when the tmp object(which is declared in GetStack()) is returned, the compiler will call constructor 3(which is a move constructor) to create a temporary and then return. So under this circumstance, I initially think the ready-to-return object tmp is a rvalue.
But when I comment the code snippet of Constructor 3 and re-run the code, the compiler just calls constructor 2 to create a temporary, so when the move constructor does not exist, tmp is treated as lvalue.
So my question is that is tmp a rvalue or a lvalue when it is ready to return from GetStack(). My compile option is g++ -fno-elide-constructors -o ref ref.cpp
This question already has answers here:
Returning a reference to a local variable in C++
(3 answers)
c++ missing construction and destruction of an object
(2 answers)
Does returning a local variable return a copy and destroy the original(nrvo)?
(1 answer)
Shouldn't there be a copy ctor invocation here? Elision disabled (no named return value optimization)
(1 answer)
Closed 5 months ago.
I was testing out the Return Value Optimizations in C++ Visual Studio 2022. I was surprised that the following program needed to call the copy constructor:
#include <iostream>
class A {
public:
A() { std::cout << "Constructor calling" << std::endl; }
A(A& a) { std::cout << "Copy Constructor calling" << std::endl; }
A(const A& a) { std::cout << "const Copy Constructor calling" << std::endl; }
};
A& getA() { A a; return a; }
int main() {
A a = getA();
return 0;
}
This outputs Constructor Calling\nCopy Constructor Calling, which means it did not get optimized with NRVO. Yet when I change the program to the following (just removed the return by reference on getA()):
#include <iostream>
class A {
public:
A() { std::cout << "Constructor calling" << std::endl; }
A(A& a) { std::cout << "Copy Constructor calling" << std::endl; }
A(const A& a) { std::cout << "const Copy Constructor calling" << std::endl; }
};
A getA() { A a; return a; }
int main() {
A a = getA();
return 0;
}
Now we get the output as just Constructor calling and no calls to the copy constructor. Why does it not get optimized when I am returning by reference?
I meet a course programming problem, which asks me to initialize the A a using passing by reference (initialize the A a in the func). How can I call A's constructor by A's reference?
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
void fun(A& a)
{
a.A::A(10); // error!
return;
}
int main()
{
A a;
fun(a);
a.print();
return EXIT_SUCCESS;
}
There is a background of this problem. The teacher want us to replicate the NRVO(named return value optimization) result.
#include <iostream>
using namespace std;
class A
{
public:
int x;
A()
{
cout << "default constructor" << endl;
x = 1;
}
A(int x)
{
cout << "constructor with param = " << x << endl;
this->x = x;
}
~A() {
cout << "destructor" << endl;
}
void print() {
cout << x << endl;
}
};
A fun() {
A a = A(10);
return a;
}
int main()
{
A a = fun();
return EXIT_SUCCESS;
}
default g++ compiler:
constructor with param = 10
destructor
if we close the NRVO:
g++ test.cpp -fno-elide-constructors
constructor with param = 10
destructor
destructor
destructor
destructor
The teacher want us to replicate the NRVO(named return value optimization) result by passing by reference.
The syntax a.A::A(10); is incorrect.
Constructor is used to create an object of a class, you cannot call it on an already existing object. Even a constructor cannot be explicitly called. It is implicitly called by the compiler.
From general-1.sentence-2:
Constructors do not have names.
Thus, you cannot call a constructor explicitly. The compiler will automatically call the constructor when an object of that class-type is created.
You can not, not like this.
A reference always points to an initialized object. So you already failed before you called the function. The "return" argument is already initialized. And you can't initialized an initialized value again, not legally.
You can cheat by calling
std::construct_at(&a, 10);
For it to really reflect NRVO you could have something like this:
void fun(A *a)
{
std::construct_at(a, 10);
}
union UninitializedA {
std::byte uninitialized[sizeof(A)];
A a;
};
int main()
{
UninitializedA u;
fun(&u.a);
u.a.print();
u.a.~A();
return EXIT_SUCCESS;
}
I got this code:
Edit: The full code:
#include <iostream>
using namespace std;
class A {
public:
A() {}
A(const A& a) {
cout << "A copy ctor" << endl;
}
virtual ~A() {
cout << "A dtor" << endl;
}
virtual void type() const {
cout << "This is A" << endl;
}
};
class B: public A {
public:
B(){}
virtual ~B() {
cout << "B dtor" << endl;
}
void type() const override {
cout << "This is B" << endl;
}
};
A f(A a) {
a.type();
return a;
}
const A& g(const A& a) {
a.type();
return a;
}
int main() {
A* pa = new B();
cout << "applying function f:" << endl;
f(*pa).type();
cout << "applying function g:" << endl;
g(*pa).type();
delete pa;
return 0;
}
I noticed when debugging the code that after making and passing a copy of *pa to f and ending the function the destructor of the copy (*pa) that was passed to f is not called.
Only when type() ended (the same line) both the copies (I assume) were erased by the destructors
I was sure that when ending the function a destructor will be called and erase the current copy which did not happened in this case. I would like to get an explanation to the order in which the constructors and desturctors are being called in the code (I am not very knowledgeable about the order when methods are involved and I could not find much about it online).
thank you.
When you do f(*pa).type();, the Copy Constructor of A is called with an A object, so it will create an A to pass into your f function. When f returns, it is a different A being returned, since it's not by ref, however, it is NOT immediately destroyed, and instead uses copy-elision to stick around until after the .type() is called.
After that point, the destructor of both the temporary object passed into f, and the temporary object return by f are destroyed, so ~A() gets called twice.
I have compiled this code with vs2011. It prints first constructor then copy constructor.
But if I change the function to return a instead of ap, it will move the object. Is this a bug or why does it behave like this? Is *ap not a rvalue?
struct A
{
A() { cout << "constructor" << endl;}
A(const A&) { cout << "copy constructor " << endl;}
void operator=(const A&) { cout << "assignment operator" << endl; }
A( A&&) { cout << "move copy constructor" << endl;}
void operator=(A&&) { cout << "move assignment operator" << endl;}
};
A func() { A a; A *ap = &a; return *ap; }
int main()
{
A a = func();
return 0;
}
*ap is an lvalue (§ 5.3.1.1, n3290) which is in general not safe for the move to happen automatically. The local variable return a; is a different case. There's no requirement for the compiler to prove that in this specific instance it would be safe. This is another good reason for not using pointers in cases where you don't really want pointer semantics.
Changing it to:
return std::move(*ap);
will cause it to be explicitly moved however.