While reading this explanation on lvalues and rvalues, these lines of code stuck out to me:
int& foo();
foo() = 42; // OK, foo() is an lvalue
int &foo();
declares a function called foo()
with return type int&
. If you call this function without providing a body then you are likely to get an undefined reference error.
In your second attempt you provided a function int foo()
. This has a different return type to the function declared by int& foo();
. So you have two declarations of the same foo
that don't match, which violates the One Definition Rule causing undefined behaviour (no diagnostic required).
For something that works, take out the local function declaration. They can lead to silent undefined behaviour as you have seen. Instead, only use function declarations outside of any function. Your program could look like:
int &foo()
{
static int i = 2;
return i;
}
int main()
{
++foo();
std::cout << foo() << '\n';
}
In that context the & means a reference - so foo returns a reference to an int, rather than an int.
I'm not sure if you'd have worked with pointers yet, but it's a similar idea, you're not actually returning the value out of the function - instead you're passing the information needed to find the location in memory where that int is.
So to summarize you're not assigning a value to a function call - you're using a function to get a reference, and then assigning the value being referenced to a new value. It's easy to think everything happens at once, but in reality the computer does everything in a precise order.
If you're wondering - the reason you're getting a segfault is because you're returning a numeric literal '2' - so it's the exact error you'd get if you were to define a const int and then try to modify its value.
If you haven't learned about pointers and dynamic memory yet then I'd recommend that first as there's a few concepts that I think are hard to understand unless you're learning them all at once.
int& foo();
is a function returning a reference to int
. Your provided function returns int
without reference.
You may do
int& foo()
{
static int i = 42;
return i;
}
int main()
{
int& foo();
foo() = 42;
}
int & foo();
means that foo()
returns a reference to a variable.
Consider this code:
#include <iostream>
int k = 0;
int &foo()
{
return k;
}
int main(int argc,char **argv)
{
k = 4;
foo() = 5;
std::cout << "k=" << k << "\n";
return 0;
}
This code prints:
$ ./a.out k=5
Because foo()
returns a reference to the global variable k
.
In your revised code, you are casting the returned value to a reference, which is then invalid.
The example code at the linked page is just a dummy function declaration. It does not compile, but if you had some function defined, it would work generally. The example meant "If you had a function with this signature, you could use it like that".
In your example, foo
is clearly returning an lvalue based on the signature, but you return an rvalue that is converted to an lvalue. This clearly is determined to fail. You could do:
int& foo()
{
static int x;
return x;
}
and would succeed by changing the value of x, when saying:
foo() = 10;
All the other answers declare a static inside the function. I think that might confuse you, so take a look at this:
int& highest(int & i, int & j)
{
if (i > j)
{
return i;
}
return j;
}
int main()
{
int a{ 3};
int b{ 4 };
highest(a, b) = 11;
return 0;
}
Because highest()
returns a reference, you can assign a value to it. When this runs, b
will be changed to 11. If you changed the initialization so that a
was, say, 8, then a
would be changed to 11. This is some code that might actually serve a purpose, unlike the other examples.