Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ return type when I don't know if it's temporary

Suppose that Foo is a rather large data structure. How should I write a const virtual function that returns an instance of Foo, if I don't know whether the inherited classes will store the instance of Foo internally; thus, allowing a return by reference. If I can't store it internally, my understanding is I can't return a const reference to it because it will be a temporary. Is this correct? The two options are:

virtual Foo foo() const { ... }
virtual Foo const & foo() const { ... }

Here's a related question but from a different angle.

like image 385
Alan Turing Avatar asked Jun 28 '11 11:06

Alan Turing


3 Answers

You're interested in the difference between a value return and a const reference return solely as a matter of optimization, but it isn't. There's a fundamentally different meaning between returning a different value each time, vs. returning a reference each time, quite possibly to the same object, which quite possibly could be modified:

const Foo &a = myobj.foo();
myobj.modify_the_foo();
const Foo &b = myobj.foo();
a == b; // do you want this to be true or false?

The caller needs to know which it is, both because the programmer needs to know the meaning and because the compiler needs to know the calling convention, so you can't mix them in different overrides of the same virtual function. If some derived classes want to do one, and some want to do the other, then that's tough luck, they can't, any more than one can return an int and another a float.

You could perhaps return a shared_ptr. That way, the derived classes that "want" to return a reference can create a shared_ptr with a deleter that does nothing (but beware - the shared_ptr will dangle if the original object is destroyed, and that's not what you normally expect from a returned shared_ptr. So if it makes sense for the Foo to outlive the object it came from then it would be better for the class to dynamically allocate it, hold it via a shared_ptr, and return a copy of that, rather than a do-nothing deleter). The derived classes that "want" to return a value can allocate a new one each time. Since Foo is "rather large", hopefully the cost of the shared_ptr and the dynamic allocation isn't too painful compared with what you'd do anyway to create a new value to return.

Another possibility is to turn Foo into a small pImpl-style class that references a rather large data structure. If everything involved is immutable, then the "want to return a reference" case can share the large data structure between multiple Foo instances. Even if it isn't, you can think about copy-on-write.

like image 112
Steve Jessop Avatar answered Nov 12 '22 00:11

Steve Jessop


I see that you haven't listed C++0x as a tag, but as reference for anyone with your needs plus access to C++0x, perhaps the best way is to return a std::unique_ptr<>.

like image 3
Johann Gerell Avatar answered Nov 12 '22 01:11

Johann Gerell


Here's one way:

struct K 
 { 
 int ii; 
 };

class I 
  { 
  virtual K &get_k(int i)=0; 
  };

class Impl : public I 
  { 
  K &get_k(int i) { kk.ii = i; return kk; } 
  K kk; 
  };

What makes it work is that you use K kk; inside the same object as data member. A constructor for Impl class might be useful too.

EDiT: changing formatting of the code

like image 1
tp1 Avatar answered Nov 12 '22 00:11

tp1