int x = 7 ; // x is an object of type int// x has an address (of type pointer to int)// an object is a region of storage that has a size, a type, and a lifetime// this object was created by the definition of the variable x of type intint& r = x ; // r is a variable, but it is not an object// the variable r refers to (is an alias for) the the object x.// r has no address of its own; and it may not occupy any storage at all// r is an alias for x, and &r yields the address of x// (references are not objects; we can't have arrays of references)// (references are not objects; we can't use new to allocate a reference)int* p = &x ; // p is an object of type pointer to int (initialised with the address of x)// p is an object; it has an address (of type pointer to pointer to int) // p is a region of storage that has a size, a type, and a lifetime// this object was created by the definition of the variable of type pointer to int

When you see the ampersand being used with the data type immediately to the left and the variable name immediately to the right, such as in the creation of a reference or in declaring functions argument list, then it is being used as the reference operator. If you see this character immediately to the left of an already existing variable then it is being used as an address of operator. This is my attempt at creating a "rule of thumb" for this and I'm confident that it's accurate. I'm sure I will be corrected if it is not.

With int myfun();, the expression myfun() yields a prvalue (a pure rvalue).
A prvalue either identifies a temporary object or, as in this example, a value that is not associated with any object.

With int& myfun();, the expression myfun() yields an lvalue.
That lvalue identifies a non-temporary object of type int.

12345678910111213

int fn1() { return 67 ; } // fineint fn2() { int i = 6 ; return i ; } // fine. // the lifetime of the local objct i is over once the function returns.// but we are not returning the object i; we are returning a prvalue initialised with iint& fn3() { return 67 ; } // *** error: 67 is not an lvalueint& fn4() { int i = 6 ; return i ; } // *** logical error: we are returning an lvalue// but the lifetime of the local objct i is over once the function returns.int& fn5() { staticint i = 6 ; return i ; } // fine: lifetime of i extends beyond the return
fn1() = 23 ; // *** error: can't assign to rvalue
fn5() = 23 ; // fine: assign to lvalue ( assign to static int i in fn5() )