17

This is a two part question. Is it ok to assign the return value of a function to a reference? Such as

Foo FuncBar()
{
    return Foo();
}

// some where else
Foo &myFoo = FuncBar();

Is this ok? Its my understanding that FuncBar() returns a Foo object and now myFoo is a reference to it.

Second part of the question. Is this an optimization? So if your doing it in a loop a lot of the time is it better to do

Foo &myFoo = FuncBar();

or

Foo myFoo = FuncBar();

And take into account the variables use, won't using the ref require slower dereferences?

Rakib
  • 7,435
  • 7
  • 29
  • 45
EddieV223
  • 5,085
  • 11
  • 36
  • 38

2 Answers2

23
Foo &myFoo = FuncBar();

Will not compile. it should be:

const Foo &myFoo = FuncBar();

because FuncBar() returns a temporary object (i.e., rvalue) and only lvalues can be bound to references to non-const.

Is it safe?

Yes it is safe.

C++ standard specifies that binding a temporary object to a reference to const lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.


Foo myFoo = FuncBar();      

Is Copy Initialization.
It creates a copy of the object returned by FuncBar() and then uses that copy to initalize myFoo. myFoo is an separate object after the statement is executed.

const Foo &myFoo = FuncBar();

Binds the temporary returned by FuncBar() to the reference myFoo, note that myFoo is just an alias to the returned temporary and not a separate object.

Rakib
  • 7,435
  • 7
  • 29
  • 45
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • -1 "Will not compile" is incorrect: it depends on the definition of `Foo`. It is important for beginner to understand that *it can compile*, even with a conforming compiler. – Cheers and hth. - Alf Oct 05 '12 at 07:14
  • 3
    @Cheersandhth.-Alf: As per standard it should not.With compiler extensions, it might. – Alok Save Oct 05 '12 at 07:16
  • 4
    @Cheersandhth.-Alf Whereas I understand that somebody could define it to `std::string*const&` or whatever rubbish, I guess it is safe to assume `typedef`s are out of the question here. No, don't say it, I know assumptions are evil. But Ok, *"should not compile according to the standard"* or something the like would indeed have been better. – Christian Rau Oct 05 '12 at 07:23
  • @ChristianRau: assumptions are evil, and the answer as-is is just plainly **wrong**. – Cheers and hth. - Alf Oct 05 '12 at 07:28
  • @Cheersandhth.-Alf: If we are not talking in terms of the rules laid out by the standard, there is no real basis for discussion. – Alok Save Oct 05 '12 at 07:29
  • @Als: you're wrong. see my answer. my answer is correct wrt. to the standard, your answer is not correct wrt. the standard. can you please make some *effort*. and, might i add, stop making actively misleading comments. – Cheers and hth. - Alf Oct 05 '12 at 07:30
  • 7
    @Cheersandhth.-Alf: You are being unduly rude.You can downvote if you feel the answer is incorrect. Your rudeness is not really appreciated nor is welcome. – Alok Save Oct 05 '12 at 07:36
  • @Als While it's true that `Foo const& myFoo = fooBar();` is safe, it's unnecessary excess verbiage, and never justified (assuming that `fooBar()` returns a `Foo`, and not a `Foo&`). – James Kanze Oct 05 '12 at 07:59
  • 4
    @Alf Could you explain your point more in detail, because I'm not really following you. The only cases I can think of off hand where `Foo& myFoo = fooBar();` would be legal are 1) if `Foo` is in fact a `typedef` to a `const`, or 2) `Foo` contains an `operator Foo&()`. Neither case would seem to be the "general" case. If I provide an empty definition of `class Foo`, the given code doesn't compile with g++, and gives the warning "nonstandard extension used" with VC++. – James Kanze Oct 05 '12 at 08:04
  • @james: right. i asked als twice to look at my answer. i ask the same of you: commentary fields are not really good for code examples etc., you find the requested "more detail" (not much!) in my answer ;-) anyway, both cases you list are part of the general case. the general case does not exclude the cases where this answer's wording is plainly wrong. – Cheers and hth. - Alf Oct 05 '12 at 08:37
  • @JamesKanze: `Foo const& myFoo = fooBar();` is justified for some definitions of `fooBar` other than the OP's. Of course your comment assumes the OP's definition. But just for completeness, those other definitions include where `fooBar` returns a reference, and when `fooBar` returns a result of type possibly derived from `Foo`. the latter is a case of polymorphism that few are aware of. it was used in Marginean's original `ScopeGuard` implementation; perhaps we should have invented a term for it, like "Marginean's trick" :-) – Cheers and hth. - Alf Oct 05 '12 at 08:42
  • 1
    @Alf Yes. Even `Foo& myFoo = fooBar();` is justified in cases where `fooBar` returns a reference; it's not unusual to initialize a reference with something like `std::map<>::operator[]`, for example. I was speaking strictly about the case where `fooBar` returns an object. – James Kanze Oct 05 '12 at 09:55
  • No mention of copy elision? The answer sounds like `Foo myFoo = FuncBar();` will create a copy of the returned `Foo` and therefore the reference version is more efficient which is not the case. – nwp Jul 24 '14 at 09:10
  • I got back to this due to an upvote of my answer. So, I was “rude” for pointing out that this answer is incorrect; well that is a purely *ad hominem* argument, which gets upvotes on SO. This answer is **incorrect in so many ways**. Just the "Will not compile" statement, it is incorrect (1) [for the Visual C++ compiler](http://rise4fun.com/Vcpp/10D), (2) [if `Foo` is a `const` type](http://coliru.stacked-crooked.com/a/87dd4492cc3f1242), (3) [if `Foo` is a reference](http://coliru.stacked-crooked.com/a/904f0a0f4084069a). It takes more work to refute BS than to offer it. I already answered this. – Cheers and hth. - Alf Jul 24 '14 at 10:03
2

You're not "assigning" to a reference, you're binding to a reference.

That's only proper when the type is const and the context is one where there is automatic lifetime extension of the temporary.

In general, when Foo isn't a const type your examples should fail to compile. Unfortunately, they may compile with one common compiler, due to language extensions implemented by that compiler. It is a good idea to try out examples (and also ordinary code!) with at least two compilers.


EDIT: as an example of investigative work before posting a question, you should have compiled the following (or very similar) with highest warning level with at least two compilers, with and without CONST defined.
struct Bar {};

#ifdef CONST
    typedef Bar const Foo;
#else
    typedef Bar Foo;
#endif

Foo FuncBar()
{
    return Foo();
}

int main()
{
    // som where else
    Foo &myFoo = FuncBar();
}

If you haven't already done so, it can be a good idea to do that now.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Are you talking about Visual Studio? Cause that's what I'm using and it's letting me do it. – EddieV223 Oct 05 '12 at 07:08
  • @EddieV223: I'm talking about Microsoft's C++ compiler, which is used by Visual Studio. However, it does warn you. – Cheers and hth. - Alf Oct 05 '12 at 07:09
  • VS2012 express doesn't warn me. – EddieV223 Oct 05 '12 at 07:10
  • 2
    You need to up the warning level to `/W4`. Always do that. – Cheers and hth. - Alf Oct 05 '12 at 07:13
  • Using VS2015 Update 3 with /w4 and it doesn't warn me in x86 or x64. – Neutrino Nov 24 '16 at 14:56
  • @Neutrino: I'm unable to reproduce with update 2 and with the latest version. Unfortunately the only online service I found that I could link to to show you, seems to use update 1 or no update. It's here: (http://rextester.com/HRDH4489). – Cheers and hth. - Alf Nov 24 '16 at 15:19
  • @Cheers: Tried a [variation on Alok's answer](http://www.theassimilationlab.com/forums/topic/16400-relz-sksequasidebuglogtest/?do=findComment&comment=445731), but no joy. Do you know of any other way? Thanks. – Laurie Stearn Feb 17 '18 at 14:15
  • @LaurieStearn: Not sure what you tried, the [link you supplied](http://www.theassimilationlab.com/forums/topic/16400-relz-sksequasidebuglogtest/?do=findComment&comment=445731) go to somewhere apparently unrelated? Or is that really the example, with a problem converting from `void`? If so then please post a **complete** example somewhere. – Cheers and hth. - Alf Feb 17 '18 at 14:29
  • @Cheers: Posted one as a temp answer here. Does it make any sense to you? – Laurie Stearn Feb 17 '18 at 14:34
  • @LaurieStearn: Yes, but please don't post "temp answer"s. There are a multitude of C++ paste bins on the net (see the section about online compilers in [the C++ tag info](https://stackoverflow.com/tags/c%2b%2b/info)). In the presented code, `prntArray` is undefined. – Cheers and hth. - Alf Feb 17 '18 at 14:37
  • @Cheers: All fixed! Thanks to the info on this thread, it wouldn't have happened! :) – Laurie Stearn Feb 17 '18 at 15:46