This question already has answers here:
return const reference vs temporary object
(3 answers)
Closed 10 months ago.
There is a simple program
#include <stdio.h>
int counter = 3;
const int& f() {
return counter++;
}
const int& g() {
return counter++;
}
const int& h(int w) {
counter = w;
return counter;
}
void main()
{
const int& x = f();
const int& y = g();
const int& z = g();
const int& t = h(123);
counter = 45678;
printf("%d %d %d %d", x, y, z, t);
}
Why its output is 5 5 5 45678? Especially, why x and y are 5? If they are still connected to the counter after initialization, why they are not 45679 or something like?
Because of post-incrementation that f and g do.
const int& f() {
return counter++;
}
const int& g() {
return counter++;
}
counter++ doesn't actually return the counter, but a temporary int object returned by operator++(int).
In C++, opreator++ has two forms.
Type& operator++(); //pre-increment
Type operator++(int); //post-increment
The int argument in the 2nd version is a dummy used to distinguish the calls, as you can't overload on return type alone
As you can see the post-increment returns by value, not reference, so you're returning a reference to a temporary variable!
You can see warning from GCC and Clang if you turn them on.
https://godbolt.org/z/f1fMP463a
So overall you invoke UB as the references you return are dangling. You ran into the worst case where it just seems to work.
Also, main must return int in C++.
Your program has undefined behaviour. The return values of f and g cease to exist at the end of the statements that call them, so x, y and z don't refer to anything after their declaration.
Just as an addition to the answers given so far: Why undefined behaviour?
Let's consider a hypothetical implementation of operator++:
template <typename T>
T& operator++(T& t) // pre-increment:
{
t = t + 1;
return t;
}
// should be clear so far...
template <typename T>
T operator++(T& t, int) // post-increment:
{
// t = t + 1; // cannot do now, how to return the old value then???
T b = t; // need to store the value in a backup copy first!
t = t + 1; // NOW we can
return t; // need to return the OLD value -> cannot return by reference either,
// it's a TEMPORARY ...
}
As you now use operator++, which itself returns a temporary, you return a reference to temporary that doesn't exist any more after after returning – undefined behaviour.
Why do you now see 5? Pure technically (still UB!): As f and g look identical they both use identical offsets to the stack's end on being called and as being called immediately one after another they both start at the same stack end – so they will store the temporary at the same stack address, which is where the reference gets bound to. Note that this finally contains the value before last increment, thus 5, not 6. h does not need any additional values on the stack, thus the value there won't get overwritten – just alike when doing the assignment, so the value is yet retained. You might have seen something totally different if for some reason h did use the stack itself...
Related
I understand the concept of references in C++, and I understand what they do when used in function parameters, but I am still very much confused on how they work with return types.
For example, when used in parameters, this code:
int main (void) {
int foo = 42;
doit(foo);
}
void doit (int& value) {
value = 24;
}
is similar to this code:
int main (void) {
int foo = 42;
doit(&foo);
}
void doit (int* value) {
*value = 24;
}
(knowing that the compiler will automatically put an asterisk in front of value every time it is used in the first code sample of doit, but in the latter you'd have to put the asterisk in yourself every time you try to use value)
So when used as a reference what does this next code (using reference in a return type) translate to? Does it return a pointer to an int? Or would it just return an int?
int main (void) {
int* foo = /*insert useful place in memory*/;
foo = doit(foo);
}
int& doit (int* value) {
//insert useful code
}
It means you return by reference, which is, at least in this case, probably not desired. It basically means the returned value is an alias to whatever you returned from the function. Unless it's a persistent object it's illegal.
For example:
int& foo () {
static int x = 0;
return x;
}
//...
int main()
{
foo() = 2;
cout << foo();
}
would be legal and print out 2, because foo() = 2 modifies the actual value returned by foo.
However:
int& doit () {
int x = 0;
return x;
}
would be illegal (well, accessing the returned value would), because x is destroyed when the method exits, so you'd be left with a dangling reference.
Returning by reference isn't common for free functions, but it is for methods returning members. For example, in the std, the operator [] for common containers return by reference. For example, accessing a vector's elements with [i] returns an actual reference to that element, so v[i] = x actually changes that element.
Also, I hope that "is essentially equal to this code" means that they're semantically sort of (but not really) similar. Nothing more.
It means that you return a pointer to the memory address where the correspondent data is, instead of the very data.
Assuming this code (to make it comparable to the first example) :
int main (void) {
int* foo = /*insert useful place in memory*/;
*foo = doit(foo);
}
int& doit (int* value) {
*value = 24;
return *value;
}
The int& is not really useful as a return type in this case, because it provides access to the variable in memory (of which you pass the pointer to the function).
Does it return a pointer to an int? Or would it just return an int?
No, it returns a reference to an int. If you want, you can look at it as a pointer which can not be nullptr.
Hmm, the best way to know the answer is to have a try...
Your codes will not pass type check, because doit will return a reference of int while you accept the return value as a pointer of int.
You can have a look at this:
#include<iostream>
using namespace std;
int& doit (int* value) {
value[0] = 3;
return value[4];
}
int main (void) {
int* foo = new int[10];
for (int i=0; i<10; i++)
foo[i] = i;
int& bar = doit(foo);
cout<<bar<<endl;
for (int i=0; i<10; i++)
cout<<foo[i]<<" ";
cout<<endl;
bar = 12;
for (int i=0; i<10; i++)
cout<<foo[i]<<" ";
cout<<endl;
return 0;
}
The variable "bar" will accept the return value, and it can be used change the content of "foo". As is mentioned by Luchian, it maybe dangerous to return a reference from the function, because the later codes might modify a value which is in the stack.
This question already has answers here:
Returning a reference to a local variable in C++
(3 answers)
Closed 8 years ago.
I'm trying to understand why the second piece of code compiles fine, given that the first doesn't.
int & test(void) {
int v = 0;
return v;
}
int main(void){
int & r = test();
return 0;
}
I understand that this doesn't work because you can't pass a reference to an automatic variable that will be deleted. It seems to me that the code below should have the same problem but it doesn't.
int & test1(int & x) {
return x;
}
int & test2(void) {
int x = 0;
return test1(x);
}
int main(void){
int & r = test2();
return 0;
}
Seems like the intermediate function is solving the problem. But why?
Just because something compiles, doesn't mean it works...
The two "alternatives" both suffer from the same exact problem; r, in main, is a dangling reference, what it refers to is long gone, and using it will lead to undefined behavior.
1st snippet
In the first example it's easy enough for the compiler to see that you are returning a reference to a local variable, which (as compilers know) doesn't make any sense.. the referred to instance will be dead when the reference reach main.
The compiler is being a good champ and tells you about the issue.
2nd snippet
In the second example you are doing the same thing, but adding a redirection in-between. A compiler got many tricks up its sleeve, but back-tracing every possible execution path to see if a developer is returning a reference to a local variable, by indirection, isn't one of them.
The compiler can't see that you are being bad, and it cannot warn you about issues it doesn't know about.
Conclusion
Returning a reference to a local variable is bad, no matter how you do it.
Think about what the compiler would have to do to catch the problem you're demonstrating. It would have to look at all callers of test1 to see whether they're passing it a local. Perhaps easy enough, but what if you insert more and more intermediate functions?
int & test1(int & x) {
return x;
}
int & test2(int & x) {
return test1(x);
}
int & test3() {
int x = 0;
return test2(x);
}
int main(void){
int & r = test3();
return r;
}
The compiler would have to look not only at all callers of test1, but then also all callers of test2. It would also have to work through test2 (imagine that it's more complex than the example here) to see whether it's passing any of its own locals to test1. Extrapolate that to a truly complex piece of code--keeping track of that sort of thing would be prohibitively complex. The compiler can only do so much to protect us from ourselves.
The both code examples are ill-formed and have undefined behaviour because local objects will be deleted after exiting the functions. So the references will be invalid.
To understand that the second example does not differ from the first example you could rewrite it the following way (insetad of calling the second function)
/*
int & test1(int & x) {
return x;
}
*/
int & test2(void) {
int x = 0;
/* return test1(x);*/
int &r = x;
return r;
}
As you see there is no any difference between the examples.
To achieve what you want you could the following way
int test()
{
int v = 0;
return v;
}
int main()
{
const int & r = test();
return 0;
}
Alright, so I have looked around online and clearly my problem is that I'm using a variable "val" here that stops existing when the function closes. Unfortunately, I haven't really found any actual solutions to my problem here. I'm sure this is an easy enough problem to solve once you know how, but I just don't have the knowledge.
In this code, just notice I'm trying to return an unsigned int val. I can't do that because the code wants a reference, not just a variable. I can't simply return val but I don't know what to do.
http://i.imgur.com/E8sf2aS.png
Thanks for the help.
Edit: sorry, I had some problems with the image, apparently I need to work on my rep.
I'm going to take a wild guess.
Foo& doStuff()
{
// blah blah
Foo val;
// ...
return val;
// val is no longer valid end of scope. Returning invalid reference.
}
Either pass in the result Foo instance to doStuff, or create a new Foo on the heap and return as pointer.
So,
void doStuff(Foo& val)
{
// blah blah
// ...
val = x;
}
or
Foo* doStuff()
{
// blah blah
Foo* val = new Foo; // dont forget to delete
// ...
return val;
}
Of course, you can return by value:
Foo doStuff()
{
// blah blah
Foo val;
// ...
return val;
}
Depending on how heavy a Foo is. Of course, since in this case a Foo is just an small int, you should simply return by value. For some cases of return by value for large/non-trivial types, a temporary copy is created (In those instances where there is no copy elision via RVO or NRVO); in these cases you might want to avoid returning large object types by value.
This code has a lot of problems, apart from being given in an image (!!!)
I guess you're trying to find the element at position pos-1 in a list, or something. The main problem referring to your question seems to be that you're first assigning val by value, then you have no reference to return. You should return n2->value directly, which should be a reference to unsigned int, like that:
const unsigned int &list::operator[](unsigned int pos) const
{
node *n1 = ???, *n2 = ???;
for (unsigned int k = 0; k < _size; k++)
{
if (k == pos)
return n2->value;
n1 = n2->next;
n2 = n1;
}
return ???;
}
Other problems remain, e.g.
why you need two node* and not just one (looking for position pos-1 directly)
how to initialize n1, n2 (somehow pointing to the head of your list; obviously new node() should not work)
what to return if input argument pos is out of range (possibly return a reference to some static variable that you can detect, or throw an exception)
For these problems, more context would be needed from your side.
Reference variables, are only valid if the object to which "refer" to, exists in memory. Passing around references to an out of scope variable, is considered undefined behavior.
This is the mistake in your code.Please correct it.
const unsigned int& list::operator[] (unsigned int pos)const
{
const unsigned int val = 0;
return val; //this is a local variable, whose scope ends here, a reference to this should not be returned
}
This is the compiler's warning, to your code.
warning: reference to local variable ‘val’ returned [enabled by default]
Please listen to compiler warnings (especially c/c++ !!), in your case simply using pass by value, would have been sufficient.
Edit:
In case the return variable, is enforced to be a reference type, and cannot be avoided, you can then extend the life of you local variable, to throughout the existence of the program by making it static.
const unsigned int& list::operator[] (unsigned int pos)const
{
static const unsigned int val = 0;
return val;
}
Th variable val is now a static local variable, whose life is throughout the program,
so pasing around references to this variable should be OK, but not recommended programming,
since a pass by value will suffice for the needs of your application.
I have done numerous searches and found a ton of examples and tutorials but still cannot figure out how to get the value when writing to the [] operator...
I feel like i'm going insane. I must be missing something very simple
as far as i can tell there is a single function for get and set and it looks something like this:
V& operator[](string K);
or this:
double &operator[](int n);
now great, we can get what is:
a[HERE]
as HERE becomes double &operator[](int HERE);
and we can easily work with it
but how do we get what is:
a[4] = HERE
C# has two very clear get and set methods with the value keyword which represents the object being assigned.
public string this[int key]
{
get
{
if(key == 1)
return "1!";
if(key == 2)
return "2!";
else
return "3!";
}
set
{
if( value == "setting") //value is a[3] = THIS
this.isSet = true;
}
}
Don't think of operator[] as a function to get or set, that might be confusing. Think of it as a normal function. In fact, let's re-write it as a normal function:
struct X
{
int arr[10];
int& getIndex(int idx) { return arr[idx]; }
};
//...
//initialize x of type X
x.getIndex(3) = 42;
The method x.getIndex(3) will return a reference to the 4-th element in the member array idx. Because you return by reference, the return value is an l-value and refers to that exact element, so you can assign a value to it, say, 42. This will modify the member x.arr[3] as the function returns an alias for that particular object.
Now you can re-write this in terms of operator[] exactly as before
struct X
{
int arr[10];
int& operator[](int idx) { return arr[idx]; }
};
and get the same result by calling
x[3];
or even
x.operator[](3);
The operator option is just syntactic sugar.
I understand the concept of references in C++, and I understand what they do when used in function parameters, but I am still very much confused on how they work with return types.
For example, when used in parameters, this code:
int main (void) {
int foo = 42;
doit(foo);
}
void doit (int& value) {
value = 24;
}
is similar to this code:
int main (void) {
int foo = 42;
doit(&foo);
}
void doit (int* value) {
*value = 24;
}
(knowing that the compiler will automatically put an asterisk in front of value every time it is used in the first code sample of doit, but in the latter you'd have to put the asterisk in yourself every time you try to use value)
So when used as a reference what does this next code (using reference in a return type) translate to? Does it return a pointer to an int? Or would it just return an int?
int main (void) {
int* foo = /*insert useful place in memory*/;
foo = doit(foo);
}
int& doit (int* value) {
//insert useful code
}
It means you return by reference, which is, at least in this case, probably not desired. It basically means the returned value is an alias to whatever you returned from the function. Unless it's a persistent object it's illegal.
For example:
int& foo () {
static int x = 0;
return x;
}
//...
int main()
{
foo() = 2;
cout << foo();
}
would be legal and print out 2, because foo() = 2 modifies the actual value returned by foo.
However:
int& doit () {
int x = 0;
return x;
}
would be illegal (well, accessing the returned value would), because x is destroyed when the method exits, so you'd be left with a dangling reference.
Returning by reference isn't common for free functions, but it is for methods returning members. For example, in the std, the operator [] for common containers return by reference. For example, accessing a vector's elements with [i] returns an actual reference to that element, so v[i] = x actually changes that element.
Also, I hope that "is essentially equal to this code" means that they're semantically sort of (but not really) similar. Nothing more.
It means that you return a pointer to the memory address where the correspondent data is, instead of the very data.
Assuming this code (to make it comparable to the first example) :
int main (void) {
int* foo = /*insert useful place in memory*/;
*foo = doit(foo);
}
int& doit (int* value) {
*value = 24;
return *value;
}
The int& is not really useful as a return type in this case, because it provides access to the variable in memory (of which you pass the pointer to the function).
Does it return a pointer to an int? Or would it just return an int?
No, it returns a reference to an int. If you want, you can look at it as a pointer which can not be nullptr.
Hmm, the best way to know the answer is to have a try...
Your codes will not pass type check, because doit will return a reference of int while you accept the return value as a pointer of int.
You can have a look at this:
#include<iostream>
using namespace std;
int& doit (int* value) {
value[0] = 3;
return value[4];
}
int main (void) {
int* foo = new int[10];
for (int i=0; i<10; i++)
foo[i] = i;
int& bar = doit(foo);
cout<<bar<<endl;
for (int i=0; i<10; i++)
cout<<foo[i]<<" ";
cout<<endl;
bar = 12;
for (int i=0; i<10; i++)
cout<<foo[i]<<" ";
cout<<endl;
return 0;
}
The variable "bar" will accept the return value, and it can be used change the content of "foo". As is mentioned by Luchian, it maybe dangerous to return a reference from the function, because the later codes might modify a value which is in the stack.