Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
187 views
in Technique[技术] by (71.8m points)

c++ - Why not always assign return values to const reference?

Let's say I have some function:

Foo GetFoo(..)
{
  ...
}

Assume that we neither know how this function is implemented nor the internals of Foo (it can be very complex object, for example). However we do know that function is returning Foo by value and that we want to use this return value as const.

Question: Would it be always a good idea to store return value of this function as const &?

const Foo& f = GetFoo(...);

instead of,

const Foo f = GetFoo(...);

I know that compilers would do return value optimizations and may be move the object instead of copying it so in the end const & might not have any advantages. However my question is, are there any disadvantages? Why shouldn't I just develop muscle memory to always use const & to store return values given that I don't have to rely on compiler optimizations and the fact that even move operation can be expensive for complex objects.

Stretching this to extreme, why shouldn't I always use const & for all variables that are immutable in my code? For example,

const int& a = 2;
const int& b = 2;
const int& c = c + d;

Besides being more verbose, are there any disadvantages?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Calling elision an "optimization" is a misconception. Compilers are permitted not to do it, but they are also permitted to implement a+b integer addition as a sequence of bitwise operations with manual carry.

A compiler which did that would be hostile: so too a compiler that refuses to elide.

Elision is not like "other" optimizations, as those rely on the as-if rule (behaviour may change so long as it behaves as-if the standard dictates). Elision may change the behaviour of the code.

As to why using const & or even rvalue && is a bad idea, references are aliases to an object. With either, you do not have a (local) guarantee that the object will not be manipulated elsewhere. In fact, if the function returns a &, const& or &&, the object must exist elsewhere with another identity in practice. So your "local" value is instead a reference to some unknown distant state: this makes the local behaviour difficult to reason about.

Values, on the other hand, cannot be aliased. You can form such aliases after creation, but a const local value cannot be modified under the standard, even if an alias exists for it.

Reasoning about local objects is easy. Reasoning about distributed objects is hard. References are distributed in type: if you are choosing between a case of reference or value and there is no obvious performance cost to the value, always choose values.

To be concrete:

Foo const& f = GetFoo();

could either be a reference binding to a temporary of type Foo or derived returned from GetFoo(), or a reference bound to something else stored within GetFoo(). We cannot tell from that line.

Foo const& GetFoo();

vs

Foo GetFoo();

make f have different meanings, in effect.

Foo f = GetFoo();

always creates a copy. Nothing that does not modify "through" f will modify f (unless its ctor passed a pointer to itself to someone else, of course).

If we have

const Foo f = GetFoo();

we even have the guarantee that modifying (non-mutable parts of) f is undefined behavior. We can assume f is immutable, and in fact the compiler will do so.

In the const Foo& case, modifying f can be defined behavior if the underlying storage was non-const. So we cannot assume f is immutable, and the compiler will only assume it is immutable if it can examine all code that has validly-derived pointers or references to f and determine that none of them mutate it (even if you just pass around const Foo&, if the original object was a non-const Foo, it is legal to const_cast<Foo&> and modify it).

In short, don't premature pessimize and assume elision "won't happen". There are very few current compilers that won't elide without explicity turning it off, and you almost certainly won't be building a serious project on them.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
...