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

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

Related

C++: What is the order of destructor call with methods?

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.

Why did the copy operator get called?

At the last line myA = foo(myOtherB);, the function will return type an object of type A, thus; it will be like saying `myA = input, But why is the copy constructor is being?
output:
B foo()
A copy ctor //what calls this?
A op=
For a copy constructor to be called we will have to use the assignment operator during initialization such as: B newB = myOtherB;
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A ctor" << endl; }
A(const A& a) { cout << "A copy ctor" << endl; }
virtual ~A() { cout << "A dtor" << endl; }
virtual void foo() { cout << "A foo()" << endl; }
virtual A& operator=(const A& rhs) { cout << "A op=" << endl; }
};
class B : public A {
public:
B() { cout << "B ctor" << endl; }
virtual ~B() { cout << "B dtor" << endl; }
virtual void foo() { cout << "B foo()" << endl; }
protected:
A mInstanceOfA; // don't forget about me!
};
A foo(A& input) {
input.foo();
return input;
}
int main() {
B myB;
B myOtherB;
A myA;
myOtherB = myB;
myA = foo(myOtherB);
}
At the last line myA = foo(myOtherB);, the function will return type an object of type B
Not true. Your function returns an object of type A by value. That means, any value you feed this object to be constructed with will be used to construct a new object of that exact type. So in other words:
int foo(float a) {
return a + 0.5;
}
int u;
u = foo(9.3);
// u has a value of 10
Don't expect u to hold a value that a int cannot.
Same thing if you use user defined types:
A foo(A& input) {
input.foo();
return input; // this expression returns a new A
// using the value of `input`
}
A myA;
myA = foo(myOtherB);
// why would `myA` be anything else than the value of an A?
So then, what happen here?
B foo()
A copy ctor //what calls this?
A op=
A foo(A& input) {
input.foo(); // prints B foo, virtual call. a reference to A that
// points to an object of subclass type B
return input; // copy `input` into the return value object
}
Then, the operator= gets called.
See cppreference
Specifically:
The copy constructor is called whenever an object is initialized (by direct-initialization or copy-initialization) from another object of the same type (unless overload resolution selects a better match or the call is elided), which includes
initialization: T a = b; or T a(b);, where b is of type T;
function argument passing: f(a);, where a is of type T and f is void f(T t);
function return: return a; inside a function such as T f(), where a is of type T, which has no move constructor.

Difference in calling a constructor with delegation, and in another constructor body

lets say I have a class with 2 constructors like so:
class Foo
{
Foo(int x);
Foo();
...
}
I know that I can call one constructor from another like Foo() : Foo(42) {}, but why shouldn't (or should) I do the following:
Foo() {
Foo(42)
}
What is the difference in these cases?
Some suggest to use an "initializer" method, called from any constructor with their respective arguments, but I am puzzled as to what happens in the case above?
Let's put this example:
#include <iostream>
class B
{
private:
int number;
public:
B()
{
B(1);
}
B(int x) : number(x)
{
std::cout << "Constructor: " << x << std::endl;
}
void print(){
std::cout << "Msg: " << number << std::endl;
}
~B(){std::cout << "Destructor: " << number << std::endl;}
};
int main() {
B b;
b.print();
return 0;
}
Output:
Constructor: 1
Destructor: 1
Msg: 1
Destructor: 1
You are destroying a second object! This is strange, what happens if we use pointers...
#include <iostream>
class B
{
private:
int* arr;
public:
B()
{
B(1);
}
B(int x)
{
std::cout << "Constructor: " << x << std::endl;
arr = new int[x];
}
void print(int n){
std::cout << "Msg: " << arr[n] << std::endl;
}
void set(int n,int val){
arr[n] = val;
}
~B()
{
std::cout << "Destructor: " << arr << std::endl;
delete[] arr;
}
};
int main() {
B b;
b.set(0,14);
b.print(0);
return 0;
}
Constructor: 1
Destructor: 0xc45480
Msg: 14
Destructor: 0xc45480
Look up the pointer addr. They are the same, this means:
We are writing in deleted memory.
We are deleting the same memory twice.
These are two serious problems. You shouldn't do that.
Expression Foo(){Foo(42);} constructs anonymous temporary object that gets destroyed immediately without changing the object being constructed anyhow, while from Foo() : Foo(42){} will initialize the object being constructed.
You should not the following:
Foo() {
Foo(42)
}
When you are in the constructor body the member variables have been just constructed. That's why in C++ initialisation list exists.
The above code is semantically wrong! You are not absolutely using
delegating construction. Instead the statement Foo(42) in the body will just create another object without assigning it to any variable (anonymous variable).
You can imagine something like:
Foo() {
Foo another_obj = Foo(42);
}
In order to use delegating constructor, you must call constructor
in the initialisation list.
Foo() : Foo(42) { }

Parenthesis vs curly braces

#include<iostream>
using namespace std;
class test
{
public:
int a,b;
test()
{
cout<<"default construictor";
}
test(int x,int y):a(x),b(y){
cout<<"parmetrized constructor";
}
};
int main()
{
test t;
cout<<t.a;
//t=(2,3);->gives error
t={2,3}; //calls paramterized constructor
cout<<t.a;
}
Output:- default construictor4196576parmetrized constructor2
why in case of the above example, parameterized constructor(even though default constructor is already called.) is called in case of {} and not in ()
I added some additional code to show what is actually happening.
#include<iostream>
using namespace std;
class test
{
public:
int a,b;
test()
{
cout << "default constructor" << endl;
}
~test()
{
cout << "destructor" << endl;
}
test(int x,int y):a(x),b(y)
{
cout << "parameterized constructor" << endl;
}
test& operator=(const test& rhs)
{
a = rhs.a;
b = rhs.b;
cout << "assignment operator" << endl;
return *this;
}
};
int main()
{
test t;
cout << t.a << endl;
//t=(2,3);->gives error
t={2,3}; //calls parameterized constructor
cout << t.a << endl;
}
Output:
default constructor
4197760
parameterized constructor
assignment operator
destructor
2
destructor
So the statement t={2,3}; is actually constructing a new test object using the parameterized constructor, calling the assignment operator to set t to be equal to the new, temporary test object, and then destroying the temporary test object. It's equivalent to the statement t=test(2,3).
use
test t(2,3);
instead of
test t;
t=(2,3);
Because parentheses are used after object declaration.

rvalue hello world missing constructor

I'm trying to know more about rvalue references but I got stuck on this simplest example:
#include <iostream>
using namespace std;
struct C {
C() { cout << "C()\n"; }
~C() { cout << "~C()\n"; }
C(const C&) { cout << "C(const C&)\n"; }
C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }
C(C&&) { cout << "C(C&&)\n"; }
C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
};
C foo() { C c; return c; }
int main()
{
const C c = foo();
return 0;
}
I've compiled it with Clang 3.2 and -std=c++11 -fno-elide-constructors (to avoid (N)RVO) but the result is surprising to me:
C()
~C() // huh?
C(C&&)
~C()
~C()
I expected exactly that except for the first ~C(). Where did it came from and what am I missing because there are 2 constructions and 3 destructions? Is the && constructor called with a destroyed object reference??
This must be a bug. The destructor for the local object constructed in foo() is being invoked before the move constructor of the receiving object. In particular, it seems a temporary is allocated but not (move-)constructed when returning by value. The following program shows this:
#include <iostream>
using namespace std;
struct C
{
C(int z) { id = z; cout << "C():" << id << endl; }
~C() { cout << "~C():" << id << endl; }
C(const C& c) { id = c.id + 1; cout << "C(const C&):" << id << endl; }
C& operator=(const C&) { cout << "operator=(const C&)\n"; return *this; }
C(C&& c) { id = c.id + 1; cout << "C(C&&):" << id << endl;}
C& operator=(C&&) { cout << "operator=(C&&)\n"; return *this; }
int id;
};
C foo() { C c(10); return c; }
int main()
{
const C c = foo();
return 0;
}
Output:
C():10
// THE TEMPORARY OBJECT IS PROBABLY ALLOCATED BUT *NOT CONSTRUCTED* HERE...
~C():10 // DESTRUCTOR CALLED BEFORE ANY OTHER OBJECT IS CONSTRUCTED!
C(C&&):4198993
~C():4198992
~C():4198993
Creating two objects inside of foo() seems to shed some more light on the issue:
C foo() { C c(10); C d(14); return c; }
Output:
C():10
C():14
~C():14
// HERE, THE CONSTRUCTOR OF THE TEMPORARY SHOULD BE INVOKED!
~C():10
C(C&&):1 // THE OBJECT IN main() IS CONSTRUCTED FROM A NON-CONSTRUCTED TEMPORARY
~C():0 // THE NON-CONSTRUCTED TEMPORARY IS BEING DESTROYED HERE
~C():1
Interestingly, this seems to depend on how the object is constructed in foo(). If foo() is written this way:
C foo() { C c(10); return c; }
Then the error appears. If it is written this way it does not:
C foo() { return C(10); }
Output with this last definition of foo():
C():10 // CONSTRUCTION OF LOCAL OBJECT
C(C&&):11 // CONSTRUCTION OF TEMPORARY
~C():10 // DESTRUCTION OF LOCAL OBJECT
C(C&&):12 // CONSTRUCTION OF RECEIVING OBJECT
~C():11 // DESTRUCTION OF TEMPORARY
~C():12 // DESTRUCTION OF RECEIVING OBJECT