7
#include<iostream>
using namespace std;

int &fun()
{
  static int x = 10;
  return x;
}
int main()
{
   fun() = 30;
   cout << fun();
   return 0;
}

Function fun() is returning value by reference but in main() method I am assigning some int to function. Ideally, a compiler should show an error like lvalue required but in above case the program works fine. Why is it so?

Walter
  • 44,150
  • 20
  • 113
  • 196
username_4567
  • 4,737
  • 12
  • 56
  • 92
  • 5
    Returning reference to static variable is lvalue (and legal). Why do you think that there should be any errors? – Dmitry Sazonov Sep 23 '13 at 16:59
  • 1
    The returned value from `fun()` is `int &` which is 'lvalue` – CS Pei Sep 23 '13 at 16:59
  • Evaluating a reference *results in* an lvalue. – Kerrek SB Sep 23 '13 at 17:00
  • 1
    I am not able to understand the code at all. What difference does it make if I am returning a reference. Is 30 assigned to x in foo? Please point me to some source which can explain such construct in C++ – username_4567 Sep 23 '13 at 17:00
  • If you want to forbid this, return either by value (`int`) or by const-reference (`const int&`). – syam Sep 23 '13 at 17:06
  • This is a method usually used in hashtable implementations. So you can do something like `hashtable[6] = "six"`. Now in the hashtable class you can have a method (hashfindfunc) that implements the square bracket operator. Therefore when this method is called and returns a reference to a string, the assignment operator is able to give a new value to the string returned. So with the same idea, if you remove the square-brackets, this is the same as `hashfindfunc(int val) = "six"` – smac89 Sep 23 '13 at 17:08
  • You want the compiler to give you an error when you write syntactically correct, but bad code? – Zac Howland Sep 23 '13 at 17:10
  • 1
    @username_4567: Yes, this code assigns `30` to the static variable in `fun`, via the returned reference. That's what references are (usually) for - to refer to something that's not in the current scope. If you don't want it to be modifiable, then return either a value (`int`) or constant reference (`int const &`). – Mike Seymour Sep 23 '13 at 17:10
  • @username_4567 Yes, it assigns `30` to the `x` in `fun`. The source you are looking for is probably any good beginners book which should explain what a reference is. See [the Definitive C++ Book Guide and List](http://stackoverflow.com/questions/388242). – Daniel Frey Sep 23 '13 at 17:11
  • if we omit static keyword from definition of fun(),it is error:"attempting to return a reference to local variable a" what is it? Why is it? – A.s. Bhullar Oct 01 '13 at 15:45
  • I understand that ``static`` variable ``x`` gets updated if we do ``fun() = 30``. But why doesn't the subsequent calls to ``fun()`` return 10? Doesn't the function gets called ? If it does, then shouldn't it return 10 because ``x = 10`` gets executed ? – Robur_131 Aug 02 '19 at 01:22
  • https://www.internalpointers.com/post/understanding-meaning-lvalues-and-rvalues-c Read this. – Adi Nov 21 '21 at 15:11

7 Answers7

7

It's loose and sloppy language to say "a function returns something". It's OK as a shorthand if you know how to work with that, but in this case you get confused.

The more correct way to think about it is that you evaluate a function call expression. Doing that gives you a value. A value is either an rvalue or an lvalue (modulo details).

When T is an object type and you evaluate a function that has return type T, you get a value of type T which is an rvalue. On the other hand, if the function has return type T &, you get a value of type T which is an lvalue (and the value is the thing bound to the reference in the return statement).

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 3
    Note that `f() = value` is error only if `f()` evaluates to an rvalue of *built-in type*, otherwise it is alright if it evaluates to rvalue of class type. (lvalue in any case is fine already) – Nawaz Sep 23 '13 at 17:15
5

Returning a reference is quite useful.

For example it's what std::map::operator[] does. And I hope you like the possibility of writing my_map[key] = new_value;.

If a regular (non-operator) function returns a reference then it's ok to assign to it and I don't see any reason for which this should be forbidden.

You can prevent assignment by returning a const X& or by returning X instead if you really want.

6502
  • 112,025
  • 15
  • 165
  • 265
4

You can rewrite the code using pointers, which might be easier to understand:

#include<iostream> 
using namespace std; 

int *fun() //fun defined to return pointer to int
{ 
    static int x = 10; 
    return &x; // returning address of static int
} 
int main() 
{ 
    *fun() = 30; //execute fun(), take its return value and dereference it,
                 //yielding an lvalue, which you can assign to. 
    cout << *fun();  //you also need to dereference here
    return 0; 
}

References can be very confusing from a syntax point of view, as the dereferencing of the underlying "pointer" is implicitly done by the compiler for you. The pointer version looks more complicated, but is clearer or more explicit in its notation.

PS: Before someone objects to me regarding references as being a kind of pointer, the disassembly for both code versions is 100% identical.

PPS: Of course this method is a quite insidious breach of encapsulation. As others have pointed out, there are uses for this technique, but you should never do something like that without a very strong reason for it.

GermanNerd
  • 643
  • 5
  • 12
2

It works becuse the result of that function is an lvalue. References are lvalues. Basically, in the whole point of returning a non-const reference from a function is to be able to assign to it (or perform other modifications of referenced object).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

In addition to other answers, consider the following code:

SomeClass& func() { ... }

func().memberFunctionOfSomeClass(value);

This is a perfectly natural thing to do, and I'd be very surprised if you expected the compiler to give you an error on this.

Now, when you write some_obj = value; what really happens behind the scenes is that you call some_obj.operator =(value);. And operator =() is just another member function of your class, no different than memberFunctionOfSomeClass().

All in all, it boils down to:

func() = value;
// equivalent to
func().operator =(value);
// equivalent to
func().memberFunctionOfSomeClass(value);

Of course this is oversimplified, and this notation doesn't apply to builtin types like int (but the same mechanisms are used).

Hopefully this will help you understand better what others have already explained in terms of lvalue.

syam
  • 14,701
  • 3
  • 41
  • 65
2

I was buffled by similar code too - at fist. It was "why the hell I assign value to a function call, and why compiler is happy with it?" I questioned myself. But when you look at what happens "behind", it does make sense.


As cpp and others poined out, lvalues are "memory locations" that have address and we can assign values to them. You can find more on the topic of lvalues and rvalues on the internet.

When we look at the function:

int& fun()
{
    static int x = 10;
    return x;
}

I moved the & to the type, so it's more obvious we are returning a reference to int.
We see we have x, which is lvalue - it has address and we can assign to it. It's also static, which makes it special - if it wasn't static, the lifetime (scope) of the variable would end with stack unwinding upon leaving the function and then the reference could point to whatever black hole exists in the universe. However as x is static, it will exist even after we leave the function (and when we come back to the function again) and we can access it outside of the function.

We are returning reference to an int, and since we return x, it's reference to the x. We can then use the reference to alter the x outside of the function. So:

int main()
{
    fun();

We just call the function. Variable x (in scope of fun function) is created, it has value of 10 assigned. It's address and value exist even after function is left - but we can't use it's value, since we don't have it's address.

    fun() = 30;

We call the function and then change the value of x. The x value is changed via the reference returned by the function. NOTE: the function is called first and only after the function call was completed, then, the assignment happens.

    int& reference_to_x = fun(); // note the &

Now we (finally) keep the reference to x returned by the function. Now we can change x without calling the function first. (reference_to_x will probably have the same address as the x have inside the fun function)

    int copy_of_x = fun(); // no & this time

This time we create new int and we just copy the value of x (via the reference). This new int has its own address, it doesn't point to the x like reference_to_x is.

    reference_to_x = 5;

We assigned x the value 5 through the reference, and we didn't even called the function. The copy_of_x is not changed.

    copy_of_x = 15;

We changed the new int to value 15. The x is not changed, since copy_of_x have its own address.

}


As 6502 and others pointed out, we use similar approach with returning references a lot with containers and custom overrides.

std::map<std::string, std::string> map = {};

map["hello"] = "Ahoj";
// is equal to
map.operator[]("hello") = "Ahoj"; // returns reference to std::string
// could be done also this way
std::string& reference_to_string_in_map = map.operator[]("hello");
reference_to_string_in_map = "Ahoj";

The map function we use could have declaration like this:

std::string& map::operator[]( const std::string& key ); // returns reference

We don't have address to the string we "stored" in the map, so we call this overridden function of map, passing it key so map knows which string we would like to access, and it returns us reference to that string, which we can use to change the value. NOTE: again the function is called first and only after it was completed (map found the correct string and returned reference to it) the assignment happens. It's like with fun() = 10, only more beatiful...

Hope this helps anyone who still woudn't understand everything even after reading other answers...

Community
  • 1
  • 1
0

L-value is a locator-value. It means it has address. A reference clearly has an address. The lvalue required you can get if you return from fun() by value:

#include<iostream>
using namespace std;

int fun()
{
  static int x = 10;
  return x;
}
int main()
{
   fun() = 30;
   cout << fun();
   return 0;
}
cpp
  • 3,743
  • 3
  • 24
  • 38