Taking address of an rvalue allowed? [duplicate] - c++

This question already has answers here:
Reference a temporary in msvc
(2 answers)
Closed 5 years ago.
I am practicing things described Here to learn the concept of lvalue and rvalue.
However, when I contrive my own example as follows, I find it compiles and runs without any error (using VS2017). I have learnt that in case 1, it is the same as calling
produceA(10).operator=(A())
and thus is legal. But, I still don't understand why case 2 and 3 are allowed. In fact, case 2 even contradicts the example given in the article. Am I actually getting the addresses of rvalues in these 2 cases? Do they lead to undefined behavior?
#include <string>
class A
{
int data = 1;
public:
A() = default;
A(int in)
: data(in)
{}
A& operator=(const A& rhs)
{
data = rhs.data;
return *this;
}
};
A produceA(int i)
{
A a(i);
return a;
}
int main()
{
// case 1
produceA(10) = A();
// case 2
A* pa = &produceA(10); // rvalue?
// case 3
std::string* pstr = &std::string("Temp"); // rvalue?
return 0;
}

&produceA(10) is the address of the anonymous temporary of type A returned back from the function. You are allowed to set a pointer to it, but that pointer is only valid for the lifetime of the entire statement.
The same can be said for &std::string("Temp"); again, this is an address of an anonymous temporary.

Related

Why does my rvalue copy constructor not work? [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 5 years ago.
I was testing my knowledge in C++ and have encountered a problem. Consider this code:
class A
{
public:
A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
A(const A& a)
: m_n(a.m_n)
{
std::cout << 'c';
}
A(A&& a){
std::cout<<'r';
}
private:
int m_n;
};
int main()
{
A a = A();
std::cout << std::endl;
return 0;
}
Clearly, the A() constructor is an rvalue, as no permanent object has been created. So I think that first I have to see "d" as output. Then we are copying the rvalue to our new object, which is yet to be initialised. I have implemented a copy constructor that accepts an rvalue as an argument but I did not see the proof (no "r" output).
Can someone explain why that is?
You're seeing a form of copy elision -- the compiler is actually required to optimize and implement A a = A() by initializing a directly with the arguments to the constructor expression (() in this case) rather than creating a temporary and copying it into a. If you want to enforce an unnamed object to be copied, you need to initialize a with an expression that is not a simple constructor call.
Note that even then, the compiler may elide construtor calls by the as-if rule (if the only visible side effects are in the copy/move constructors/destructors. Your best bet to "force" a call to the move ctor is to use a named value and std::move:
A a1;
A a2 = std::move(a1);

return type for overriding operator= [duplicate]

This question already has answers here:
Return type of assignment operator
(1 answer)
Why must the copy assignment operator return a reference/const reference?
(8 answers)
Closed 5 years ago.
Let's say there is this code:
struct Tester
{
int value = 0;
void operator=(const Tester& original)
{
value = original.value;
}
};
Purpose of overriding operator= is to use it like normal assignment operator. This function would definitely take parameter's "value" and put it to host object's "value".
However, other programmers suggest to use:
Tester& operator=(const Tester& original)
{
value = original.value;
return *this;//edited
}
with return type of Tester as reference. The first function would do exactly the same. Why and how would that be useful?
The first function would do exactly the same
Not quite. The second version you posted omits a return statement. So it has undefined behavior. But once it's fixed:
Tester& operator=(const Tester& original)
{
value = original.value;
return *this;
}
It can be used to chain assignments:
a = b = c;
Something your void version cannot be used for.

Initializer list and const& - is this legal C++ [duplicate]

This question already has answers here:
Does a const reference class member prolong the life of a temporary?
(6 answers)
Closed 6 years ago.
Consider:
#include <iostream>
#include <vector>
class A
{
public:
const int& i;
};
class B
{
public:
const std::vector<int>& i;
};
int main()
{
A a = { 3 };
std::cout << a.i << std::endl;
B b = { { 1, 2 } };
std::cout << b.i[0] << " " << b.i[1] << std::endl;
}
On VS2015 update 3, this crashes in runtime on the last line because the vector b.i is empty; on gcc (4.9.2) this runs OK and shows the expected output (3 1 2). So on VS it 'works' (does what I expected) for an int, but not a vector.
Is this a VS bug or is it just an accident that it works on gcc?
The first is OK, temporary's lifetime is extended when assigned to a reference. Second is UB AFAIK, because { { 1, 2 } } is a std::initializer_list<>, not a std::vector<> directly. Prolonging lifetime temporary objects is not transitive (i.e., it lasts till the end of current function, in this case, the constructor), only local ones get prolonged.
It works on gcc by accident, because this is definitely undefined behavior.
In order to satisfy B's initialization, the compiler needs to construct a temporary vector<int>. Since the reference to that vector is const, the compiler does not see a problem with using it to initialize B. However, the temporary object becomes invalid as soon as the initialization is over, so accessing it by reference outside of initialization is undefined behavior.

C++ Identify different of operator = [duplicate]

This question already has answers here:
Difference between returning reference vs returning value C++
(3 answers)
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 8 years ago.
MyClass& operator=(const MyClass& other)
{
//Implement
return *this;
}
MyClass operator=(const MyClass& other)
{
//Implement
return *this;
}
void operator=(const MyClass& other)
{
//Implement
}
When I test these methods, the result is the same. In almost book I see that the first method(MyClass&) is used more than the second method. what's different between them? Which method is really right and fast? One method return address and the second return value.
When I test these methods, the result is the same.
it depends on how you test your class, ie.:
void foo (MyClass& a);
MyClass a1;
MyClass a2;
foo(a1 = a2);
in case of second operator implementation (returning MyClass), above code (inside foo) will not modify a1 instance, but a temporary value.
In almost book I see that the first method(MyClass&) is used more than
the second method.
and thats correct, its more correct to return reference to *this in assignment operator
what's different between them? Which method is really right and fast?
first version is faster and correct because it does not do any copy of your object, also its more proper because thats how primitive types behave, ie:
int n = 0;
int k = 10;
(n = k) = 1;
std::cout << n;
here on output you will get 1, because (n = k) returns reference to n.

C++ copy constructor causing code not to compile ( gcc )

I have the following code which doesn't compile. The compiler error is:
"error: no matching function to call B::B(B)",
candidates are B::B(B&) B::B(int)"
The code compiles under either of the following two conditions:
Uncommenting the function B(const B&)
change 'main' to the following
int main()
{
A a;
B b0;
B b1 = b0;
return 0;
}
If I do 1, the code compiles, but from the output it says it's calling the 'non const copy constructor'.
Can anyone tell me what's going on here?
using namespace std;
class B
{
public:
int k;
B()
{
cout<<"B()"<<endl;
}
B(int k)
{
cout<<"B(int)"<<endl;
this->k = k;
}
/*B(const B& rhs) {
cout<<"Copy constructor"<<endl;
k = rhs.k;
}*/
B(B& rhs)
{
cout<<"non const Copy constructor"<<endl;
k = rhs.k;
}
B operator=(B& rhs)
{
cout<<"assign operator"<<endl;
k = rhs.k;
return *this;
}
};
class A
{
public:
B get_a(void)
{
B* a = new B(10);
return *a;
}
};
int main()
{
A a;
B b0 = a.get_a(); // was a.just();
B b1 = b0 ;
return 0;
}
I've done some extra reading into this, and as I suspected all along, the reason why this occurs is due to return value optimization. As the Wikipedia article explains, RVO is the allowed mechanism by which compilers are allowed to eliminate temporary objects in the process of assigning them or copying them into permanent variables. Additionally, RVO is one of the few features (if not the only) which are allowed to violate the as-if rule, whereby compilers are only allowed to make optimizations only if they have the same observable behaviours as if the optimization were never made in the first place -- an exemption which is key in explaining the behaviour here (I admittedly only learned of that exemption as I was researching this question, which is why I was also confused initially).
In your case, GCC is smart enough to avoid one of the two copies. To boil your code down to a simpler example
B returnB()
{
B a;
B* b = &a;
return *b;
}
int main()
{
B c = returnB();
return 0;
}
If one follows the standard and does not perform RVO, two copies are made in the process of making c -- the copy of *b into returnB's return value, and the copy of the return value into c itself. In your case, GCC omits the first copy and instead makes only one copy, from *b directly into c. That also explains why B(B&) is called instead of B(const B&) -- since *b (a.k.a. a) is not a temporary value, the compiler doesn't need to use B(const B&) anymore and instead chooses the simpler B(B&) call instead when constructing c (a non-const overload is always automatically preferred over a const overload if the choice exists).
So why does the compiler still give an error if B(const B&) isn't there? That's because your code's syntax must be correct before optimizations (like RVO) can be made. In the above example, returnB() is returning a temporary (according to the C++ syntax rules), so the compiler must see a B(const B&) copy constructor. However, once your code is confirmed to be grammatically correct by the compiler, it then can make the optimization such that B(const B&) is never used anyway.
EDIT: Hat tip to Charles Bailey who found the following in the C++ standard
12.2 [class.temporary]: "Even when the creation of the temporary is avoided,
all the semantic restrictions must be
respected as if the temporary object
was created."
which just reinforces and confirms the need for a copy constructor taking a reference to const when temporaries are to be copied for construction (irrespective of whether or not the constructor is actually used)
Your get_a() function returns an object B, not a reference (but leaks the newly-created B object). Also, for assignments to work as you're doing, you need to make sure your assignment operator and copy constructors are both taking const B& arguments — then B b1 = b0 will work in this case. This works:
class B
{
public:
int k;
B() { cout<<"B()"<<endl; }
B(int k) {
cout<<"B(int)"<<endl;
this->k = k;
}
B(const B& rhs) {
cout<<"non const Copy constructor"<<endl;
k = rhs.k;
}
B operator=(const B& rhs) {
cout<<"assign operator"<<endl;
k = rhs.k;
return *this;
}
};
class A {
public:
B* get_a(void) {
B* a = new B(10);
return a;
}
B get_a2(void) {
B a(10);
return a;
}
};
int main() {
A a;
B b0 = *a.get_a(); // bad: result from get_a() never freed!
B b1 = a.get_a2(); // this works too
return 0;
}
Short explanation: functions that return by value create a temporary object which is treated as constant and therefore cannot be passed to functions accepting by reference, it can only be passed to functions accepting const reference. If you really allocate an object in get_a(), you should really be returning a pointer (so that you remember to delete it, hopefully) or in the worst case - a reference. If you really want to return a copy - create the object on the stack.
Long explanation: To understand why your code doesn't compile if there is only "non-const copy constructor"1, you need to be familiar with the terms lvalue and rvalue. They originally meant that rvalues can only stand on the right side of operator = (assignment) while lvalues can stand also on the left side. Example:
T a, b;
const T ac;
a = b; // a can appear on the left of =
b = a; // so can b => a and b are lvalues in this context
ac = a; // however, ac is const so assignment doesn't make sense<sup>2</sup>, ac is a rvalue
When the compiler is performing overload resolution (finding which overload of a function/method best match the provided arguments) it will allow lvalues to match parameters passed by value3, reference and const reference types. However, it will match rvalues only against value3 and const reference parameters. And that's because in some sense, since rvalues cannot be put on the left side of operator =, they have read-only semantic, and when it shouldn't be allowed to modify them. And when you accept a parameter through non-const reference, it's implied that you'll somehow change this parameter.
The last piece of the puzzle: temporary objects are rvalues. Function returning by value creates a temporary object with very limited lifespan. Because of its limited lifespan it's considered const, and is therefore a rvalue. And this rvalue doesn't match functions with parameters by non-const reference. Examples:
void f_cref(const A& a) { std::cout << "const ref" << std::endl; }
void f_ref(A& a) { std::cout << "non-const ref" << std::endl; }
A geta() { return A(); }
A a;
const A ac;
f_ref(a); // ok, a is a lvalue
f_ref(ac); // error, passing const to non-const - rvalue as lvalue - it's easy to spot here
f_cref(a); // ok, you can always pass non-const to const (lvalues to rvalues)
f_ref(geta()); // error, passing temporary and therefore const object as reference
f_cref(geta()); // ok, temporary as const reference
Now you have all the information to figure out why your code doesn't compile. Copy constructor are like regular functions.
I have oversimplified things a bit, so better, more complete and correct explanation can be found at this excellent Visual C++ Studio Team blog post about rvalue references, which also addresses the new C++ 0x feature "rvalue references"
1 - there's no such thing as non-const copy constructor. The copy constructor accepts const reference, period.
2 - you can probably put const object on the left of = if it has its operator = declared const. But that would be terrible, terrible, nonsensical thing to do.
3 - actually, you wouldn't be able to pass const A by value if A doesn't have a copy constructor - one that accepts const A& that is.
The line B b1 = b0; is the culprit. This line requires calling a copy constructor. You could fix the code by writing B b1(b0) instead, or by defining a copy constructor, which takes a const B&, but not a B&.