What does "int& foo()" mean in C++?
Asked Answered
D

9

121

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

I tried it in g++, but the compiler says "undefined reference to foo()". If I add

int foo()
{
  return 2;
}

int main()
{
  int& foo();
  foo() = 42;
}

It compiles fine, but running it gives a segmentation fault. Just the line

int& foo();

by itself both compiles and runs without any problems.

What does this code mean? How can you assign a value to a function call, and why is it not an rvalue?

Duhl answered 7/4, 2016 at 13:23 Comment(13)
You're not assigning a value to a function call, you're assigning a value to the reference it returns.Reactivate
@FrédéricHamidi assigning a value to the object that the returned reference is referring toInculpable
@M.M, the reference is the object -- it's an alias.Reactivate
Yes it's an alias for the object; the reference is not the objectInculpable
Ah. This brings back fond memories when I tried to program in c++ and failed. This still isn't clear to me, even after reading the 5 or so answers. OTOH, I now remember why Java was such hot stuff in the 90's, even though it was slower than slugs.Divider
@RoyFalk local function declarations are a pox, personally I'd be happy to see them removed from the language. (Excluding lambdas of course)Inculpable
It's weird that this code compiles with GCC versions 4.7.4, 4.8.5, 4.9.3 and 5.3.0, but fails to compile with Clang 3.7.1 with error: functions that differ only in their return type cannot be overloaded. You should probably file a bug report for GCC here.Gluteus
@Gluteus clang is right, but as much as I know compiler is not obligated to detect all variants of invalid code.Zelikow
@Zelikow Yes, but I think the GCC folks would like to know if their compiler compiles dangerous code without giving any warnings even with -Wall -Wextra -Wshadow.Gluteus
There's already a GCC bug for this.Gluteus
Wow this is like 3 questions in one - reference vs object? undefined reference? local function declaration?Stoller
@jotik: Oh, right, yeah, sorryGassy
Reviewing all answers and comments posted, I believe only @Gluteus addresses the OP's issue viz., "It compiles fine" <-- that is what I consider to be OP's issue.Matz
M
176

The explanation is assuming that there is some reasonable implementation for foo which returns an lvalue reference to a valid int.

Such an implementation might be:

int a = 2; //global variable, lives until program termination

int& foo() {
    return a;
} 

Now, since foo returns an lvalue reference, we can assign something to the return value, like so:

foo() = 42;

This will update the global a with the value 42, which we can check by accessing the variable directly or calling foo again:

int main() {
    foo() = 42;
    std::cout << a;     //prints 42
    std::cout << foo(); //also prints 42
}
Manikin answered 7/4, 2016 at 13:26 Comment(2)
Reasonable as in operator[] ? Or some other member acces methods?Yvoneyvonne
Given the confusion about refrences, it's probably best to remove static local variables from the samples. The initialization adds unnecessary complexity, which is a hurdle to learning. Just make it a global.Dieselelectric
P
76

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.

Pimiento answered 7/4, 2016 at 22:8 Comment(5)
Is there a reason to write int a{3} instead of int a = 3? This syntax does not seem appropriate to me.Myocarditis
@AlexPetrenko that's universal initialization. I happened to be using it in the example I had handy. Nothing inappropriate about itPimiento
I know what this is. a = 3 just feels so much cleaner. Personal preference)Myocarditis
Just like declaring static inside a function might confuse some people, I also believe using universal initialisation is as likely to.Bankable
@AlekseiPetrenko In case of int a = 1.3 it implicitly converts to a = 1. Whereas int a {1.3} result in error: narrowing conversion.Benita
I
32
int& foo();

Declares a function named foo that returns a reference to an int. What that examples fails to do is give you a definition of that function that you could compile. If we use

int & foo()
{
    static int bar = 0;
    return bar;
}

Now we have a function that returns a reference to bar. since bar is static it will live on after the call to the function so returning a reference to it is safe. Now if we do

foo() = 42;

What happens is we assign 42 to bar since we assign to the reference and the reference is just an alias for bar. If we call the function again like

std::cout << foo();

It would print 42 since we set bar to that above.

Ipa answered 7/4, 2016 at 13:29 Comment(0)
I
15

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';
}
Inculpable answered 7/4, 2016 at 13:29 Comment(0)
U
10

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;
}
Unfrock answered 7/4, 2016 at 13:26 Comment(0)
W
7

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.

Wilhelmstrasse answered 7/4, 2016 at 13:28 Comment(0)
G
5

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.

Gratianna answered 7/4, 2016 at 13:36 Comment(0)
K
4

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;
Karly answered 7/4, 2016 at 13:28 Comment(0)
S
3

The function you have, foo(), is a function that returns a reference to an integer.

So let's say originally foo returned 5, and later on, in your main function, you say foo() = 10;, then prints out foo, it will print 10 instead of 5.

I hope that makes sense :)

I'm new to programming as well. It's interesting to see questions like this that makes you think! :)

Swipe answered 7/4, 2016 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.