The problem with return arguments that are const references

I know why the following does not work, so I am not asking why. But I feel bad that it seems to me that this is very complicated programming.

#include <iostream> #include <string> using namespace std; string ss("hello"); const string& fun(const string& s) { return s; } int main(){ const string& s = fun("hello"); cout<<s<<endl; cout<<fun("hello")<<endl; } 

The first cout will not work. the second cout will be.

I am worried about the following:

It is impossible to imagine a situation where the method developer wants to return an argument that is a reference to a constant and inevitable?
I think this is entirely possible. What would you do in C ++ in this situation?

Thanks.

+2
c ++ pass-by-reference
source share
8 answers

I think this is a slight weakness of C ++. There is an unsuccessful combination of two factors:

  • A function return is valid only as long as its argument is absent.
  • Implicit conversion means that a function argument is not an object that it might seem to be.

I have no sympathy for people who do not think about the life of objects to which they have pointers / links. But the implicit conversion, which is certainly a language function with subtle pros and cons, does not make the analysis very easy here. Implicit conversion is sometimes bad news, so the explicit keyword exists. But the problem is not that converting to string bad at all, it is just bad for this function used in this wrong way.

The author of the function can actually disable the implicit conversion by defining overload:

 const char *fun(const char *s) { return s; } 

This change in itself means that code that was previously bad is working. Therefore, I think it is a good idea to do this in this case. Of course, this does not help if someone defines a type that fun has never heard of, and which has operator std::string() . In addition, fun not a realistic function, and for more useful routines, you may not want to provide an equivalent that works on char* . In this case, void fun(const char *); at least forces the caller to explicitly point to the string, which may help them use this function correctly.

Alternatively, the caller may notice that he provides a char* and returns a reference to string . It seems to me that this is a free lunch, so alarm clocks should call where this line comes from and how long it will last.

+2
source share

In C ++, it is important to set the lifetime of objects. One of the common methods is the choice of "owner" for each object. The owner is responsible for ensuring that the object exists as long as it is needed, and deleting it when it is not needed.

Often the owner is another object that contains the object belonging to it in the instance variable. Other typical ways to deal with this is to make it a global, static member of a class, local variable, or use a pointer with reference counting.

In your example, there is no clear ownership of the string object. It does not belong to the main () function, because it is not a local variable, and there is no other owner.

+5
source share

The method is valid and is used all the time. However, in the first example, you convert const char* to a temporary std::string and try to return it, which does not match returning a reference to const for an object stored elsewhere. In the second example, you do the same thing, but you use the result before the temporary destruction, which in this case is legal but dangerous (see your first case.)

Update. Let me clarify my answer. I say that the problem is the creation of temporary and improper handling of the lifetimes of the created objects. The technique is good, but it (along with many other good methods) requires the fulfillment of pre- and post-conditions of functions. Part of this burden rests with the function programmer (who must document it) and partly with the client.

+3
source share

I feel your pain. I found other situations where returning a const link was correct, but there were other ugly problems.

Fortunately, thin gotcha is solved in C ++ 0x. Always come back by value. New move constructors will make everything faster as you wish.

+3
source share

Yes, I agree that there are situations when this is a problem.

I would use a reference-counting pointer to "solve" it.

0
source share

Is it impossible to imagine a situation where a method developer wants to return an argument that is a reference to a constant and is inevitable?

Wrong question to ask, really. All you have to do is whether to include the returned link in the parameter (passed by reference) and the document, which is part of the interface . (This also often becomes apparent.) Let the caller decide what to do, including temporarily into an explicit object and then pass it on.

This is common and is required to document the lifetime of returned pointers and references, for example, for std :: string :: data.

What would you do in C ++ in this situation?

Often you can pass and return by value instead . This is usually done using things like std :: copy (for the destination iterator in this case).

0
source share

In the upcoming C ++ standard, r-value references can be used to keep your temporary objects alive and fix the problem you have.

You can look for perfect redirects and move constructors.

0
source share

I think you are asking for problems in C ++ 98 :)

This can be solved in two ways. First, you can use a generic pointer. In this case, the memory will be managed automatically using shared_ptr , and you're done! But in most cases this is a bad decision. Because you really do not share memory between many links. auto_ptr is the true solution to this problem if you use a bunch all the time. auto_ptr requires one small important improvement that is not available in C ++ 98 to be really useful, that is: Move Semantic!

The best solution is to allow the movement of property between links using the r-value links that are in C ++ 0x. So your code snippet will look (not sure if the syntax is correct):

 string fun(const string& s) { return s; // return a copy of s } .... string s = fun("Hello"); // the actual heap memory is transfered to s. // the temporary is destroyed, but as we said // it is empty, because 's' now owns the actual data! 
0
source share

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


All Articles