jlunavtgrad
jlunavtgrad

Reputation: 1015

Return Value Optimization and private copy constructors

I've written a simple linked list because a recent interview programming challenge showed me how rusty my C++ has gotten. On my list I declared a private copy constructor because I wanted to explicitly avoid making any copies (and of course, laziness). I ran in to some trouble when I wanted to return an object by value that owns one of my lists.

class Foo
{
   MyList<int> list;  // MyList has private copy constructor

   public:
      Foo() {};
};

class Bar
{
   public:
      Bar() {};

      Foo getFoo()
      {
         return Foo();
      }
};

I get a compiler error saying that MyList has a private copy constructor when I try to return a Foo object by value. Should Return-Value-Optimization negate the need for any copying? Am I required to write a copy constructor? I'd never heard of move constructors until I started looking for solutions to this problem, is that the best solution? If so, I'll have to read up on them. If not, what is the preferred way to solve this problem?

Upvotes: 1

Views: 1157

Answers (3)

Steve Jessop
Steve Jessop

Reputation: 279235

The basic problem is that return by value might copy. The C++ implementation is not required by the standard to apply copy-elision where it does apply. That's why the object still has to be copyable: so that the implementation's decision when to use it doesn't affect whether the code is well-formed.

Anyway, it doesn't necessarily apply to every copy that the user might like it to. For example there is no elision of copy assignment.

I think your options are:

  • implement a proper copy. If someone ends up with a slow program due to copying it then their profiler will tell them, you don't have to make it your job to stop them if you don't want to.
  • implement a proper move, but no copy (C++11 only).
  • change getFoo to take a Foo& (or maybe Foo*) parameter, and avoid a copy by somehow mutating their object. An efficient swap would come in handy for that. This is fairly pointless if getFoo really returns a default-constructed Foo as in your example, since the caller needs to construct a Foo before they call getFoo.
  • return a dynamically-allocated Foo wrapped in a smart pointer: either auto_ptr or unique_ptr. Functions defined to create an object and transfer sole ownership to their caller should not return shared_ptr since it has no release() function.
  • provide a copy constructor but make it blow up somehow (fail to link, abort, throw an exception) if it's ever used. The problems with this are (1) it's doomed to fail but the compiler says nothing, (2) you're enforcing quality of implementation, so your class doesn't work if someone deliberately disables RVO for whatever reason.

I may have missed some.

Upvotes: 3

pmr
pmr

Reputation: 59811

The standard explicitly states that the constructor still needs to be accessible, even if it is optimized away. See 12.8/32 in a recent draft.

I prefer making an object movable and non-copyable in such situations. It makes ownership very clear and explicit.

Otherwise, your users can always use a shared_ptr. Hiding shared ownership is at best a questionable idea (unless you can guarantee all your values are immutable).

Upvotes: 5

jpalecek
jpalecek

Reputation: 47762

The solution would be implementing your own copy constructor that would use other methods of MyList to implement the copy semantics.

... I wanted to explicitly avoid making any copies

You have to choose. Either you can't make copies of an object, like std::istream; then you have to hold such objects in pointers/references, since these can be copied (in C++11, you can use move semantics instead). Or you implement the copy constructor, which is probably easier then solving problems on each place a copy is needed.

Upvotes: 2

Related Questions