Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Does the function below show undefined behavior?

42 views
Skip to first unread message

Ayrosa

unread,
Dec 11, 2015, 11:53:45 AM12/11/15
to
int& foo(int&& i){ return i; }

I know that this other function is OK

int& f(int& i){ return i; }

But what about the function on the top above, which has an rvalue ref argument?

Ayrosa

unread,
Dec 11, 2015, 12:11:53 PM12/11/15
to
Maybe I should rewrite my question to be more precise: does the code below show undefined behavior?

int& foo(int&& i){ return i; }

int main()
{
int i = foo(2);
}

Alf P. Steinbach

unread,
Dec 11, 2015, 12:19:41 PM12/11/15
to
It's OK in itself.

But you need to keep in mind that when you call it with a temporary as
argument, that temporary ceases to exist at the end of the full-expression.

Cheers & hth.,

- Alf

Ayrosa

unread,
Dec 11, 2015, 12:38:07 PM12/11/15
to
On Friday, December 11, 2015 at 3:19:41 PM UTC-2, Alf P. Steinbach wrote:
> It's OK in itself.
>
> But you need to keep in mind that when you call it with a temporary as
> argument, that temporary ceases to exist at the end of the full-expression.
>
> Cheers & hth.,
>
> - Alf

But isn't the argument i an lvalue inside foo. If this is the case, the reference returned by the function f is already undefined when the function returns. That is, the value assigned to i in main() is undefined.

Assume for the moment that foo has an argument A&&, where A is a class type, with a move constructor.

A& foo(A&& a) { return a; }

int main()
{
A aa = foo(A());
}

Inside the function foo, a is an lvalue, onto which the temporary A() is moved. By the time the function returns, the object a of type A is destroyed and the value assigned to aa is undefined.

Martin Shobe

unread,
Dec 11, 2015, 1:40:27 PM12/11/15
to
On 12/11/2015 11:37 AM, Ayrosa wrote:
> On Friday, December 11, 2015 at 3:19:41 PM UTC-2, Alf P. Steinbach wrote:
>> It's OK in itself.
>>
>> But you need to keep in mind that when you call it with a temporary as
>> argument, that temporary ceases to exist at the end of the full-expression.
>>
>> Cheers & hth.,
>>
>> - Alf
>
> But isn't the argument i an lvalue inside foo.

Yes.

> If this is the case, the reference returned by the function f is already undefined when the function returns. That is, the value assigned to i in main() is undefined.

No. The reference returned is for the object passed in. It will remain
valid until that object's lifetime ends.

> Assume for the moment that foo has an argument A&&, where A is a class type, with a move constructor.
>
> A& foo(A&& a) { return a; }
>
> int main()
> {
> A aa = foo(A());
> }
>
> Inside the function foo, a is an lvalue, onto which the temporary A() is moved. By the time the function returns, the object a of type A is destroyed and the value assigned to aa is undefined.

No. The reference remains valid until the end of the full expression, so
it's fine unless the move constructor tries to keep a reference to it's
argument or one of it's sub-objects.

Martin Shobe

Ayrosa

unread,
Dec 11, 2015, 1:54:06 PM12/11/15
to
On Friday, December 11, 2015 at 4:40:27 PM UTC-2, Martin Shobe wrote:
>
> No. The reference remains valid until the end of the full expression, so
> it's fine unless the move constructor tries to keep a reference to it's
> argument or one of it's sub-objects.

I have to disagree. The stack is unwound just after the execution of the return statement inside foo, and before the assignment is made to aa in main(). So, when the assignment is made, the reference returned by the function is already undefined.

See these two answers in Stackoverflow about this:

http://stackoverflow.com/a/26321330/411165

and

http://stackoverflow.com/a/8808865/411165

Paavo Helde

unread,
Dec 11, 2015, 2:03:25 PM12/11/15
to
Ayrosa <jaay...@gmail.com> wrote in
news:bdda3cb9-2b88-45a7...@googlegroups.com:
The stackoverflow examples are about a local variable defined inside the
function. In your example the variable is created outside of the function
and thus lives longer than the function call.

Cheers
Paavo

Ayrosa

unread,
Dec 11, 2015, 2:24:12 PM12/11/15
to
On Friday, December 11, 2015 at 5:03:25 PM UTC-2, Paavo Helde wrote:
> The stackoverflow examples are about a local variable defined inside the
> function. In your example the variable is created outside of the function
> and thus lives longer than the function call.

We have the same situation here. Don't forget that the temporary `A()` is moved onto the local variable `a` inside `foo`, which is an lvalue. So the two questions in SO say exactly the same thing, i.e., the return value of `foo` is a reference to a local variable, which has already beed destructed, by the time the assignment is made to the variable `aa` in main().

The word move is important here and should be understood as something similar to a copy (more efficient than a copy of course).

The same thing happens in the first example, the prvalue 2 is "moved" into the local variable `i` inside `foo` and by the time the function returns, the reference returned by the function is already undefined, before the assignment is made to the local variable `i` in `main()`.

Tobias Müller

unread,
Dec 11, 2015, 2:54:49 PM12/11/15
to
No, nothing is moved here.
There is exactly one objects and it lives on the stack of main. Everything
else is just references to that.

RValue references _don't_ imply moving. They are a useful tool for defining
move constructors, but not more.
They behave just like normal references, except that they only bind to
rvalues. But they are still just references.

Tobi

Chris Vine

unread,
Dec 11, 2015, 3:25:14 PM12/11/15
to
On Fri, 11 Dec 2015 19:52:08 -0000 (UTC)
Tobias Müller <tro...@bluewin.ch> wrote:
[snip]
> No, nothing is moved here.
> There is exactly one objects and it lives on the stack of main.
> Everything else is just references to that.
>
> RValue references _don't_ imply moving. They are a useful tool for
> defining move constructors, but not more.
> They behave just like normal references, except that they only bind to
> rvalues. But they are still just references.

In the code given:

A& foo(A&& a) { return a; }

int main()
{
A aa = foo(A());
}

there are two objects not one. First, the temporary create by the call
to A(), which lasts until the end of the full expression in which it is
situated, and secondly the named object aa which lasts until the end of
main() and which is initialized by the temporary. The question is
whether the construction of aa is within the full expression within
which the temporary is created so that the initialization is valid. As
I understand it, it is, so the code is valid. If that is right (and to
be honest I would never bother looking it up because I would never
write code like this), this also means that the compiler might elide the
two objects into one, depending on whether the default constructor and
move constructor of A have observable side effects.

Chris

Chris Vine

unread,
Dec 11, 2015, 3:31:44 PM12/11/15
to
On Fri, 11 Dec 2015 20:24:50 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> In the code given:
>
> A& foo(A&& a) { return a; }
>
> int main()
> {
> A aa = foo(A());
> }
>
[snip]
> ... If that is right (and to be honest I would never bother
> looking it up because I would never write code like this), this also
> means that the compiler might elide the two objects into one,
> depending on whether the default constructor and move constructor of
> A have observable side effects.

Or possibly even if they do have observable side effects if RVO on
constructing 'aa' is permitted in these circumstances; and I do not know
if it is or it isn't (I suspect it isn't, but that is a guess).

Tobias Müller

unread,
Dec 11, 2015, 3:42:45 PM12/11/15
to
Oops yes that was sloppy.

What I really wanted to write is, that _in the context of foo_ there's only
one object that lives _outside_ of foo.
But in foo itself there are no temporaries and nothing is moved around.
Especially there is no local _object_ 'a' as implied by Ayrosa.

Whether A() lives long enough for the final assignment or not is a
different question.

Tobi

Chris Vine

unread,
Dec 11, 2015, 3:48:56 PM12/11/15
to
However, in the case posited it is _the_ question!

Chris

Tobias Müller

unread,
Dec 11, 2015, 3:56:56 PM12/11/15
to
That was the question of the OP, yes.
But I was trying to correct a false statement made by Ayrosa:
> Don't forget that the temporary `A()` is moved onto the local variable
> `a` inside `foo`, which is an lvalue. So the two questions in SO say
> exactly the same thing, i.e., the return value of `foo` is a reference to
> a local variable, which has already beed destructed, by the time the
> assignment is made to the variable `aa` in main().

Tobi

Chris Vine

unread,
Dec 11, 2015, 3:59:42 PM12/11/15
to
On 11 Dec 2015 20:38:29 GMT
r...@zedat.fu-berlin.de (Stefan Ram) wrote:
> r...@zedat.fu-berlin.de (Stefan Ram) writes:
> >Ayrosa <jaay...@gmail.com> writes:
> >>int& foo(int&& i){ return i; }
> >Fundamental types have no move constructors,
> >so the argument value should be /copied/ into »i« AFAIK.
>
> An experiment with an implementation showed, however,
> that, instead, the rvalue reference behaves like a
> lvalue reference, not like an object:
>
> #include <iostream>
> #include <ostream>
> #include <utility>
>
> #define PRINT(X) ::std::cout << "&" #X " = " << &X << '\n';
>
> void demo( int && b )
> { PRINT(b); }
>
> int main()
> { int a = 8;
> PRINT(a);
> demo( ::std::move( a )); }
>
> &a = 0x01414c
> &b = 0x01414c

The rvalue reference behaves like a reference, and the lvalue reference
behaves like a reference. What else could you possibly have expected?

Chris

Martin Shobe

unread,
Dec 11, 2015, 4:02:31 PM12/11/15
to
On 12/11/2015 2:38 PM, Stefan Ram wrote:
> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>> Ayrosa <jaay...@gmail.com> writes:
>>> int& foo(int&& i){ return i; }
>> Fundamental types have no move constructors,
>> so the argument value should be /copied/ into »i« AFAIK.
>
> An experiment with an implementation showed, however,
> that, instead, the rvalue reference behaves like a
> lvalue reference, not like an object:

Which is good since an rvalue reference is not an object.

Martin Shobe

Alf P. Steinbach

unread,
Dec 12, 2015, 12:24:44 AM12/12/15
to
On 12/11/2015 8:23 PM, Ayrosa wrote:
>
> Don't forget that the temporary `A()` is moved onto the local variable `a`
> inside `foo`, which is an lvalue.

Ignoring that `a` is not a variable:

A variable is not and cannot be an lvalue. The term "lvalue" (and the
term "rvalue") refers to an /expression/ in the source code. It's not
meaningful to apply it to variables.

Paavo Helde

unread,
Dec 12, 2015, 5:11:16 AM12/12/15
to
Ayrosa <jaay...@gmail.com> wrote in
news:f12a3863-206f-4ad5...@googlegroups.com:

> On Friday, December 11, 2015 at 5:03:25 PM UTC-2, Paavo Helde wrote:
>> The stackoverflow examples are about a local variable defined inside
>> the
>
>> function. In your example the variable is created outside of the
>> function
>
>> and thus lives longer than the function call.
>
> We have the same situation here. Don't forget that the temporary `A()`
> is moved onto the local variable `a` inside `foo`, which is an lvalue.

>>>> A& foo(A&& a) { return a; }

No, a is not an object as mentioned by others. Everything declared with &
or && is a reference which only refers to some other object.

An object is a region of memory. A reference is not an object (you cannot
take its address, for example).

Cheers
Paavo
0 new messages