Why return by reference disables NRVO [duplicate] - c++

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?

Related

is the ready-to-return object a lvalue or rvalue? [duplicate]

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

how to initialize a class object reference in C++ to simulate NRVO?

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;
}

Why isn't the copy or move constructor called C++ [duplicate]

This question already has answers here:
How does guaranteed copy elision work?
(2 answers)
What are copy elision and return value optimization?
(5 answers)
Closed 11 months ago.
struct Foo {
Foo(): { cout << "Default Ctor" << endl; }
Foo(const Foo& foo) { cout << "Copy Ctor" << endl; }
Foo(Foo&& foo) { cout << "Move Ctor" << endl; }
};
Foo test() {
Foo temp;
return temp;
}
int main() {
Foo f1; // Default Ctor
Foo f2(f1); // Copy Ctor
Foo f3(test()); // ????
return 0;
}
The f3 object is successfully created, but neither copy constructor nor move constructor is called.
How does this work?

Object return in C++ [duplicate]

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?

Returning pointer by value does not move the object

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.