Why use the non-reference constant when const reference lifetime is the length of the current region

So, in C ++, if you assign the return value of a function to a const reference, then the lifetime of this return value will be the scope of that link. For instance.

MyClass GetMyClass() { return MyClass("some constructor"); } void OtherFunction() { const MyClass& myClass = GetMyClass(); // lifetime of return value is until the end // of scope due to magic const reference doStuff(myClass); doMoreStuff(myClass); }//myClass is destructed 

Thus, it seems that wherever you usually assigned a return value from a function to a const object, you could instead assign a reference to a constant. Is there ever a case in a function where you would like to not use the link in the task and use the object instead? Why do you want to write a line:

 const MyClass myClass = GetMyClass(); 

Edit: my question was confused by a couple people, so I added the definition of the GetMyClass function

Edit 2: please do not try to answer the question if you have not read this: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

+6
source share
6 answers

If the function returns an object (not a link), you need to make a copy in the calling function [although optimization steps can be taken, which means that the object is written directly to the resulting storage, where the copy ends, in accordance with the β€œas is” principle].

In the code example const MyClass myClass = GetMyClass(); this β€œcopy” object is named myclass , not a temporary object that exists but is not named (or visible unless you are looking at machine code). In other words, whether you declare a variable for it or not, there will be a myclass object inside the function that calls myclass - it's just a matter of whether you make it visible or not.

Edit2: The original const solution will look similar (not identical, and it's really just written to explain what I mean, you can't do this):

  MyClass __noname__ = GetMyClass(); const MyClass &myclass = __noname__; 

It is just that the compiler generates the __noname__ variable behind the scenes without actually telling you about it.

By making const MyClass myclass , the object becomes visible and clears what is happening (and that GetMyClass returns a COPY of the object, and not a link to some existing object).

On the other hand, if GetMyClass really returns a link, then this is certainly the right thing.

In some compilers, using a link can even add extra memory that is read when using an object, since the link is a β€œpointer” [yes, I know the standard does not say that, but please do me a favor and show me the compiler before complaining, which does NOT implement links as pointers with extra sugar to make them taste sweeter], so to use a link, the compiler must read the link value (pointer to the object), and then read the value inside the object from this pointer. In the case without a link, the object itself is "known" to the compiler as a direct object, not a link, while maintaining this additional reading. Of course, most compilers optimize such an additional link MOST of time, but it cannot always do this.

+4
source

One reason may be that this link may confuse other readers of your code. Not everyone knows that the lifetime of an object extends to the volume of a link.

+1
source

Semantics:

 MyClass const& var = GetMyClass(); 

and

 MyClass const var = GetMyClass(); 

really different. Generally speaking, you would only use it first when the function itself returns a link (and is required to return a link by semantics itself). And you know that you need to pay attention to the life of the object (which is not under your control). You use the second one when you want to own (copy) an object. Using the second in this case is misleading, can lead to surprises (if the function also returns a reference to an object that was previously destroyed) and probably a little less efficient (although in practice I expected to generate exactly the same code if GetMYClass returns a value).

+1
source

I do not understand what you want to achieve. The reason T const& can be bound (on the stack) to T (by value) that is returned from the function is to make it possible for another function to accept this temporary value as an argument of T const& . This prevents the need for congestion. But the return value must be built anyway.

But today (with C ++ 11) you can use const auto myClass = GetMyClass(); .

Edit: As an example of what might happen, I will introduce something:

 MyClass version_a(); MyClass const& version_b(); const MyClass var1 =version_a(); const MyClass var2 =version_b(); const MyClass var3&=version_a(); const MyClass var4&=version_b(); const auto var5 =version_a(); const auto var6 =version_b(); 
  • var1 initialized to version_a()
  • var2 initialized with a copy of the object to which the link returned by version_b() belongs
  • var3 contains a constant reference to temporary which returns and extends its service life
  • var4 initialized with the link returned from version_b()
  • var5 is the same as var1
  • var6 the same as var4

They are semantically different. var3 works for the reason I gave above. Only var5 and var6 automatically save the return.

0
source

Performance

Since most modern compilers copy (and move), both versions should have the same efficiency:

 const MyClass& rMyClass = GetMyClass(); const MyClass oMyClass = GetMyClass(); 

In the second case, either a copy or a move is required semantically, but it can be deleted using [class.copy] / 31. The slight difference is that the former works for non-copyable immovable types.

Mats Peterson and James Kanze noted that link access may be slower for some compilers.


Lifetime

Links must be valid in their entire area, like objects with automatic storage. Of course, this β€œshould” must be done by the programmer. Therefore, for the IMO reader, there is no difference in the lifespan implied by them. Although, if there was a mistake, I would probably look for dangling links (not trusting the source code / application for life for the link).

In the case where GetMyClass can be changed (reasonably) to return a link, you need to make sure that the lifetime of this object is sufficient, for example.

 SomeClass* p = /* ... */; void some_function(const MyClass& a) { /* much code with many side-effects */ delete p; a.do_something(); // oops! } const MyClass& r = p->get_reference(); some_function(r); 

of property

A variable by directly calling an object like const MyClass oMyClass; , clearly indicates that I own this object. Consider mutable members: if you change them later, it will not immediately clear the reader, which is normal (for all changes) if it was declared as a link.

In addition, for reference, it is not obvious that the object to which it refers does not change. The const reference means that you will not modify the object, and not that no one will change the object (*). The programmer would need to know that this link is the only way to access this object, by looking for the definition of this variable.

(*) Disclaimer: try to avoid undisguised side effects

0
source

there is a serious problem that the destructor is actually being called. Check out the Gotw88, Q3 and A3. I put everything in a small test program (Visual-C ++, so forgive stdafx.h)

 // Gotw88.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> class A { protected: bool m_destroyed; public: A() : m_destroyed(false) {} ~A() { if (!m_destroyed) { std::cout<<"A destroyed"<<std::endl; m_destroyed=true; } } }; class B : public A { public: ~B() { if (!m_destroyed) { std::cout<<"B destroyed"<<std::endl; m_destroyed=true; } } }; B CreateB() { return B(); } int _tmain(int argc, _TCHAR* argv[]) { std::cout<<"Reference"<<std::endl; { const A& tmpRef = CreateB(); } std::cout<<"Value"<<std::endl; { A tmpVal = CreateB(); } return 0; } 

The output of this small program is as follows:

 Reference B destroyed Value B destroyed A destroyed 

Here is a little explanation of the setting. B is derived from A, but both do not have a virtual destructor (I know this is WTF, but this is important here). CreateB () returns the value of B by value. Currently, it calls CreateB and first saves the result of this call in a reference to const of type A. Then CreateB is called, and the result is stored in a value of type A.

The result is interesting. First, if you store by reference, the correct destructor is called (B); if you store by value, the wrong one is called. The second - if you store in a link, the destructor is called only once, this means that there is only one object. By value, it leads to 2 calls (for different destructors), which means that there are 2 objects.

My advice is to use the const link. At least in Visual C ++, this leads to less copying. If you are unsure of your compiler, use and adapt this test program to test the compiler. How to adapt? Add a copy / move constructor and copy operator.


I quickly added copy and assignment operators for classes A and B

 A(const A& rhs) { std::cout<<"A copy constructed"<<std::endl; } A& operator=(const A& rhs) { std::cout<<"A copy assigned"<<std::endl; } 

(same for B, just replace each capital A with B)

this leads to the following result:

 Reference A constructed B constructed B destroyed Value A constructed B constructed A copy constructed B destroyed A destroyed 

This confirms the results given above (note that A constructed results from B built as B are obtained from A and, therefore, the constructor As is called whenever the constructor Bs is called).

Additional tests: Visual C ++ also accepts a non-constant reference with the same result (in this example) as a reference to a constant. In addition, if you use type auto as type, the correct destructor is called (of course), and optimization of the return value works and, in the end, the same result as a constant reference (but, of course, auto has type B, not A )

0
source

Source: https://habr.com/ru/post/951384/


All Articles