tags:

views:

513

answers:

6

I have a function for which I cannot change the function parameters. I need to return a const reference to a std::string created in this function. I tried to use boost shared_ptr, but this doesn't work. Why? How do I make this work?

const std::string& getVal(const std::string &key) {
  boost::shared_ptr<std::string> retVal(new std::string());
  ... //build retVal string with += operator based on key
  return *retVal;
}
+3  A: 

You can't return a reference to a local variable from a function with c++. Although in c++0x this is possible.

Allocate the string on the heap and manually cleanup later:

If you cannot change the function's interface, then you will need to create it on the heap and then manually delete it after.

//Remember to free the address that getVal returns
const std::string& getVal(const std::string &key) {
  std::string *retVal = new std::string();
  ... //build retVal string with += operator based on key
  return *retVal;
}

Same solution but not manually:

Since the above will eventually lead to a memory leak if you forget to free it manually. I would recommend to wrap this call into a class and use RAII. I.e. in the constructor, call getVal and set a member of this class to point to it. In the destructor of your class you would delete it.

Why the code you gave with shared_ptr does not work:

shared_ptr works via reference counting. Since you are destroying the only shared_ptr object (by scope), there are no references left and the memory will be freed. To get this to work you'd have to return a shared_ptr, but you said you cannot do this.

Brian R. Bondy
+3  A: 

You would need to return a boost shared_ptr, not a std::string. As soon as the function exits that shared_ptr on the stack will go out of scope and because there's only one reference it will be deleted. You would also need to return a copy not a reference.

You should never return a reference to a stack variable because again, as soon as the function exists it will be deleted.

If you cannot change the return type then the only (icky) option would be to allocate the string on the heap, return a reference to the pointer, and make sure the calling code knows it's a pointer and later deletes it.

E.g.

const std::string& getVal(const std::string &key) {
  return *(new std::string("Something"));
}

// get the val, remember we must delete this pointer!
std::string* pString = &getVal("SomeKey");
delete pString;
Andrew Grant
I'm not sure it would be legal to delete the address of the result of getVal.
Evan Teran
it's legal if the return is a reference to something that was allocated on the heap.
Andrew Grant
+2  A: 

This is not a boost issue. You cannot return a reference to a local variable, as the variable will cease to exist once the function terminates. In a case like this, you should really return a value. However, one hack you can use is to make the local variable static, but this may well cause concurrency and other problems.

anon
A: 

Since you can't change the function's signature, you can return a reference to a static variable:

const std::string& getVal(const std::string &key) {
    static std::string retVal;
    ... //build retVal string with += operator based on key
    return retVal;
}

There are serious problems with this approach though. It is inherently not-thread safe. Even if your app is not multi-threaded, it can cause problems depending on how long the caller holds on to the reference.

If your app is not multi-threaded and each call to getVal() immediately uses or copies the string, then you can probably get away with this. But I would strongly recommend just returning a std::string.

Ferruccio
Not sure how this ended at -1. The existing signature already states that a reference is returned. The thread and lifetime problems follow from that reference and that signature. Using function statics do not make it a lot worse.
MSalters
A: 

Since what you want is the const reference, you could return by value and make the caller receive it with const reference.

Herb Sutter GotW 88

A: 

Another point to consider is that the standard allows a compiler to optimize away copies if it is possible. The standard refers to this a copy elision, however most people know it as "Named Return Value Optimisation" see here.

The basic concept is that where possible the compiler will construct the local object directly on to the object that is the result of the function call:

class A {};

A foo () {
  A a;          // #1
  return a;
}

void bar () {
  A b = foo();  // #2
}

In the above example 'a' will be constructed in the same location as 'b' in bar, and so when the function returns no copy is needed at all. In conclusion it might be worth while testing your compiler to see if it performs this optimization. The standard reference for this is 12.8/15 if your interested.

Richard Corden