What is the difference between declaring a variable auto and declaring it auto&&?

Hi,

I have two variables for a general function f():

1
2
auto value = f();
auto&& other = f();



what is the difference between them?
Last edited on
other is a reference. If f() returns a reference you'll need to be careful when using other so that the object that the reference refers is still alive.
what happens if f() does not return a reference? is other valid?
what happens if f() does not return a reference? is other valid?

A temporary object is created (via "temporary materialization") and the reference binds it.

The lifetime of the temporary is then extended to match the lifetime of the reference, so the reference continues to refer to a valid thing until its own lifetime is over.
Last edited on
Such reference has to be const? I.e. the auto handles that?

Why auto&& and not auto& ?


More generically, the things around auto in the declaration restrict or adjust the type of the created variable.
keskiverto wrote:
Such reference has to be const?

Non-const rvalue references also extend the lifetime of temporaries.
Last edited on
"Non-const rvalue references also extend the lifetime of temporaries."

even if f() returns a reference to a local variable inside f()?

That is:

1
2
3
4
5
6
int& f()
{
   int value=5;
   return  value;
}
auto&& ret = f()


so here, does ret extend the lifetime of variable value or a copy of it?
Last edited on
I did run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int& f()
{
   int value=5;
   return  value;
}
int main()
{
    int& ret = f();
    std::cout << ret << '\n';
    ret = 42;
    int two = f();
    std::cout << ret << '\n';
}

on cpp.sh and got:
main.cpp:7:12: warning: reference to stack memory associated with local variable 'value' returned [-Wreturn-stack-address]

and the output:
5
5

So the memory on stack was overwritten (by second call to f() ).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

int& f()
{
   int value=5;
   return  value;
}
int g()
{
   int val=7;
   return  val;    
}
int main()
{
    int& ret = f();
    std::cout << ret << '\n';
    ret = 42;
    int two = g();
    std::cout << ret << '\n';
}

5
7


That smells like UB to me.
No, lifetime extension only apply when you bind it directly to the temporary. My mental model is that the reference needs to be a local variable (or global) but I'm not sure what the standard actually says about struct members and there might be other corner cases. Whether lifetime extension applies to function argument is not something worth thinking about because the temporary is guaranteed to live for as long as the parameter is in use even without lifetime extension because it's not destroyed until after the function returns.
Last edited on
Why auto&& and not auto& ?


I had to look this one up. && is specifically a reference to a rvalue, usually a temporary and most of the examples bind to results of computations (eg a reference to (x+y). It is a special syntax just for that purpose alone. & is now being called an lvalue reference, which should not be applied to temporary objects.

I agree you have UB there. g() should not change ret, but it does, probably because your & reference bound it to a memory location that was reused, probably because f and g are virtually identical signatures so the placement of their local val variables will be in the same spot.

I think the point is that had you used && instead, it should have worked as expected. You must remove the & off f() to make it work, allowing it to bind.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

int f()
{
   int value=5;
   return  value;
}
int g()
{
   int val=7;
   return  val;    
}
int main()
{
    int&& ret = f();
    std::cout << ret << '\n';
    ret = 42;
    int two = g();
    std::cout << ret << '\n';
}


as expected:

5
42
Last edited on
Registered users can post here. Sign in or register to post.