3
SomeStruct getSomeStruct();

const SomeStruct a = getSomeStruct();
const SomeStruct &b = getSomeStruct();

I understand the difference between passing by value vs by reference vs by const reference. My question is, is the above example the same as when passing arguments to a function? So the assignment of b is faster because it doesn't have to copy data, unlike the assignment of a?

Tirafesi
  • 1,297
  • 2
  • 18
  • 36
  • It depends on the signature of `getSomeStruct` itself. But yes, assuming that the full signature is `const SomeStruct& getSomeStruct()` then `b` is avoiding a copy. – Cory Kramer Apr 16 '20 at 12:09
  • 1
    note that in both cases there is no assignment, only initialization. – user7860670 Apr 16 '20 at 12:18
  • @CoryKramer I added the signature of the function in the post – Tirafesi Apr 16 '20 at 12:22
  • 1
    See discussion in [Function in C++ returns by value or by reference?](https://stackoverflow.com/questions/7456778/function-in-c-returns-by-value-or-by-reference) and [Which is more efficient: Return a value vs. Pass by reference?](https://stackoverflow.com/questions/33994995/which-is-more-efficient-return-a-value-vs-pass-by-reference) – Richard Chambers Apr 16 '20 at 12:24
  • 1
    The initialization of `a` doesn't require a copy either - the object will be created in exactly the same way as the (temporary) object that `b` binds to. Modern compilers are very clever. – molbdnilo Apr 16 '20 at 12:28

2 Answers2

3

So the assignment of b is faster because it doesn't have to copy data, unlike the assignment of a?

No. There is no difference in regards to copying.

What you are doing is binding a reference to a temporary object. The lifetime of the temporary object is in this particular case extended beyond the full expression to match the lifetime of the reference. The behaviour of the program is effectually the same as if you had not used a reference. Using the reference here has the downside that programmers may be confused by its meaning unless they are aware of this lifetime extension rule.

There is no reason to use a reference like this when you know that the function does not return a reference. If the function did return a reference, then you would avoid copying by using a reference. Whether copying is faster or slower than indirection through a reference depends on the type.

A case where lifetime extension is useful is within a template where you don't know whether a function returns a reference or a reference wrapper (which is an object i.e. not a reference, but behaves like a reference in some regards due to overloaded operators). The temporary lifetime extension allows both cases to behave the same way.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

In extended eeroika answer, there are exceptions to the lifetime of extension rule:

  • A temporary bound to a return value of a function in a return statement is not extended. This value is destroyed immediately after the return expression finishes execution and will lead to a dangling reference
  • a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
  • a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing that new-expression, not as long as the initialized object. If the initialized object outlives the full expression, its reference member becomes a dangling reference.

Now if you are using version c++14 or lower you also have:

  • a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as long as the object exists. (note: such initialization is ill-formed as of DR 1696).

And with c++20 they have added:

  • a temporary bound to a reference in a reference element of an aggregate initialized using direct-initialization syntax (parentheses) as opposed to list-initialization syntax (braces) exists until the end of the full expression containing the initializer.

These all are coming form this website: CPP Reference

You want to be aware of these exceptions as well to prevent use of a dangling reference.