I have a C++ AuthenticatingProxy class instance with the below method. This method creates a Response object, which is then updated with state, and then returned. Because of the internals of the Response object it cannot be copied (i.e. I cannot simply return by value).
const Response& AuthenticatingProxy::Get(const std::string& host,
const std::string& path,
const http_headers& headers)
{
static Response response;
// do the HTTP call, and set response's state here
return response;
}
Many calls can occur on this method in the AuthenticatingProxy class, so having a static variable in here is not ideal. Could someone please suggest a solution that returns a reference that is not destroyed on the function exiting? Thanks.
The nearest I've got in my research are best practices pages that mention "Return by reference, not value, where a large object is created" but I'm yet to find an example! All the examples are for references that are passed in and then returned, or for int& style references - not ones where the object instances are created within the function itself.
I think the solution may exist in having the function return response as a value, but in the calling code using a const variable to capture it. Not sure if that only works for int though - all examples I find use basic types. Any help greatly appreciated.
There are three storage classes in C++. Static, automatic and dynamic.
Automatic objects are destroyed when the scope is exited, so you must not return a reference to a local automatic variable.
You can return a reference to a static, but as you say, it's not appropriate for you.
The only option left is dynamic. However, dynamic objects must be destroyed somehow, and when you return a reference to a dynamically created object, it's not clear to the caller that it should take care of it's destruction. So, this technically possible, but there is no good solution to the memory management†. Also, dynamic allocation can be expensive.
† Not when you insist returning a reference. But if you return a
std::unique_ptr
instead, this problem disappears.Let's revisit the option of automatic variables then. I said that you must not return a reference to a local automatic variable. It's perfectly OK to return a reference to an automatic variable from higher scope. How do you access such variable? Using a reference of course!
This is a typical solution when dynamic allocation is too expensive and when the compiler does not support move semantics. In this solution, no object is actually created in the function. It simply sets the data for an object created by the caller. The object can actually have any storage type, it won't matter.
Best solution however, is to return by value. You say that
Response
is not copyable. But since C++11, that doesn't prevent returning it by value as long as the type is movable. Even the move is elided by most compilers.You are describing a problem that existed in C++03, but which no longer exists.
Take for example the old loved
std::fstream
.Basically, deep inside*, it held a file descriptor/handle that is used to read/write from/to the file.
Because of the nature and design of C++, the
fstream
destructor closed that file handle in order to clean up the object and prevent handle leaks.Because of the internals of the
fstream
object, it could not be returned by value. Returning it by value meant to somehow prevent the destructor from closing that file handle, or to make the copy constructor duplicate that handle in some sort. Having cross platform solution is extremly difficult, if not impossible. Not to mention that copying the internal buffer is just plain wrong.So up until C++11, you couldn't return
fstream
by value.Then move semantics was invented.
In C++11, you can just move the object instead of copying it. In the
fstream
example, the move constructor shallow copies the file handle, while invalidating the original file-handle pointer. The original object destructor would inspect the original file-handle, see that it is invalid and skip closing it.This is the idiomatic solution for your problem. While you cannot copy the object, you can definitely implement move semantics for it. The internals of the object will be moved to the return value while the original object remains "empty" in some sort.
Good explanations of move semantics can be found in this SO answer: What are move semantics?
If that solution is not possible as well, declare the object in the dynamic memory storage (the "heap") and return it by some smart pointer.
Don't ever do it even if you can.
*yes, the file handle can be stored in the
streambuff
object instead, I'm describing the problem in a nutshell.Thanks to all your great hints, I've found one answer...
(Although as others have mentioned, it leaves the caller with the problem of memory management. This is fine for my needs.)
This is described on this page: http://www.codeproject.com/Articles/823658/Using-auto-for-declaring-variables-of-move-only-ty
Here is my new method declaration:-
Thus I'm returning a pointer, but using std::move and auto in the above manner returns me a pointer that I can manage in my calling code.
EDIT:-
Ok so based on feedback, I now have this in AuthenticatingProxy.cpp:-
My intermediate Connection class is doing this:-
(Note: I've had to remove the const-ness from my proxy and connection classes otherwise I'd get a compilation issue about using a deleted copy constructor)
And my client code (main) is doing this:-
This seems to work fine, and incorporates all the advice on this thread. Is this now correct? If so, thanks everyone for your help!