Reputation: 53
I've tried to understand the semantics of c++ temporary objects lifetime extension. I've tried to simulate simple situation and was a bit surprised.
Below I'm providing my code.
#include <iostream>
struct C
{
C(const int new_a) { a = new_a; };
int a = 0;
};
C return_num()
{
C num(20);
std::cout << "From func(): num = " << num.a << ", by adress: " << &num.a << std::endl;
return num;
}
void pass_num(const C& num)
{
std::cout << "From func(): num = " << num.a << ", by adress: " << &num.a << std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main(): num = " << ext_num.a << ", by adress: " << &ext_num.a << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main(): num = " << num.a << ", by adress: " << &num.a << std::endl;
pass_num(num);
}
}
Here is the main question: return_num()
works curiously from my point of view, cause I expected that address of the variable, which I'm trying to output in main
, would be the same as internally in return_num()
. Could you please explain me why it is not?
For example in pass_num()
output address matches the external address which I got in main
.
Here is example output:
Lifetime extention:
From func(): num = 20, by adress: 0x7fff44fc8b4c
From main(): num = 20, by adress: 0x7fff44fc8b70
Passing by reference:
From main(): num = 20, by adress: 0x7fff44fc8b6c
From func(): num = 20, by adress: 0x7fff44fc8b6c
Upvotes: 4
Views: 344
Reputation: 490
Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.) rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state.
I changed the below in your code and I hope it is working as expected. I changed int a
to int* a
#include <iostream>
class C
{
public:
int *a;
C( int new_a)
{
a = new int();
*a = new_a;
};
C(const C& rhs) { std::cout << "Copy " << std::endl; this->a = rhs.a; }
C(C&& rhs):a(std::move(rhs.a))
{
std::cout << "Move!!" <<"Address resource a " << &(*a) << ", Address of
resource rhs.a" << &(*rhs.a) << std::endl; rhs.a = nullptr;
std::cout << "Value of a:: " << *a << std::endl;
}
};
C return_num()
{
C num(20);
std::cout << "From return_num(): num = " << *num.a << ", Address of resource a :
"<< &(*num.a)<< std::endl;
return (std::move(num));
}
void pass_num(const C& num)
{
std::cout << "From pass_num(): num = " << *num.a << ", by adress: " << &num.a <<
std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main() 1 : num = " << *(ext_num.a) << ", by resource
adress: " << &(*ext_num.a) << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main() 2 : num = " << *num.a << ", by adress: " << &num.a
<< std::endl;
pass_num(num);
}
return 0;
}
The above code produces below output:
Lifetime extention:
From return_num(): num = 20, Address of resource a : 0x7fffeca99280
Move!!Address resource a 0x7fffeca99280, Address of resource rhs.a0x7fffeca99280
Value of a:: 20
From main() 1 : num = 20, by resource adress: 0x7fffeca99280
Passing by reference:
From main() 2 : num = 20, by adress: 0x7ffff466f388
From pass_num(): num = 20, by adress: 0x7ffff466f388
I hope it helps!
Upvotes: 2
Reputation: 182769
I suspect that taking the address of a member is inhibiting the optimization because the compiler doesn't know how to deal with all the possible edge cases. Eliminating taking the address of the member makes the optimization work.
#include <iostream>
struct C
{
C(const int new_a) { a = new_a; };
int a = 0;
struct C* t = this;
};
C return_num()
{
C num(20);
std::cout << "From func(): num = " << num.a << ", by adress: " << num.t << std::endl;
return num;
}
void pass_num(const C& num)
{
std::cout << "From func(): num = " << num.a << ", by adress: " << num.t << std::endl;
}
int main()
{
std::cout << "\nLifetime extention:" << std::endl;
{
const C& ext_num = return_num();
std::cout << "From main(): num = " << ext_num.a << ", by adress: " << ext_num.t << std::endl;
}
std::cout << "\nPassing by reference:" << std::endl;
{
C num(20);
std::cout << "From main(): num = " << num.a << ", by adress: " << num.t << std::endl;
pass_num(num);
}
}
Lifetime extention:
From func(): num = 20, by adress: 0x7ffd61f48a50
From main(): num = 20, by adress: 0x7ffd61f48a50Passing by reference:
From main(): num = 20, by adress: 0x7ffd61f48a90
From func(): num = 20, by adress: 0x7ffd61f48a90
Upvotes: -2
Reputation: 543
Imagine this function:
int getNumber(){
int num = 10;
return num;
}
This function does not return num
as an object, it returns a no-named copy of it (r-value, if you will) with the same value. Therefore, it has a different address.
The same thing happens with your return_num
function.
Upvotes: 1