Virtual move constructor

Virtual constructor: recap

Most experienced C++ developers should be familiar with the virtual constructor idiom, which allows us to duplicate a polymorphic object where we do not know its exact type, only the base.

It is usually called clone and can be implemented and used as shown in this rather contrived example:

struct fruit {
	virtual ~fruit() {}

	virtual std::unique_ptr<fruit> clone() const = 0;
};

struct apple
	: fruit
{
	virtual std::unique_ptr<fruit> clone() const {
		return std::unique_ptr<fruit>( new apple(*this) );
	}
};

// orange, pear, roddenberry etc...

int main() {
	const fruit& f = apple();
	auto fc = f.clone();
}

Note that when using unique_ptr we lose the ability to have covariant return types, since unique_ptr< apple > is not derived from unique_ptr< fruit >. In my experience I’ve never needed a derived-type copy out of clone (outside of unit testing anyway), so the covariance offers me nothing, whereas the unique_ptr gives me that nice warm fuzzy feeling 🙂

Virtual move constructor

Now, it may seem odd to want a virtual move constructor – why would I want to move an object’s content into a new object rather than continue using the old object? I’ll give the use case below, but for now just take as read that it might be helpful 🙂

It’s rather easy to implement if you already know the virtual constructor idiom, as we only need to remove the constness and generate an rvalue reference to *this.

Building on the above example we have:

struct fruit {
	virtual ~fruit() {}

	virtual std::unique_ptr<fruit> clone() const = 0;
	virtual std::unique_ptr<fruit> move_clone() = 0; // clearly non-const
};

struct apple
	: fruit
{
	virtual std::unique_ptr<fruit> clone() const; /* as before */

	virtual std::unique_ptr<fruit> move_clone()  {
		return std::unique_ptr<fruit>( new apple( std::move(*this) ) );
	}
};

If we’re wanting a virtual move constructor then we’re likely to want to create move constructors for the classes as well, but I’ve left that out from here.

But…why?

So I know the concept of a virtual move constructor seems odd, but I’ve got a use case I can think of no better solution for than this:

  • We have a non-copyable polymorphic buffer object that we wish to move ownership of into a functor, which will be run exactly once passing ownership elsewhere. Due to the constraints of the system (and following standard library convention) this functor must be copyable.
  • To safely enable the functor to be copyable we convert the unique_ptr< buffer > into a shared_ptr inside the functor.
  • Once the functor has run we wish to release ownership of the buffer from the shared_ptr…but shared_ptr doesn’t provide a release method.
  • So we ‘move clone’ the shared buffer instance to release its internally-owned resources to a new instance, allowing shared_ptr to destroy the now ‘zombified’ original instance.

I couldn’t find any mentions of ‘virtual move constructor’ about, so it’s likely I’m doing something so ridiculous that nobody else has thought of it. I can’t see any alternatives that wouldn’t require making buffer copyable and maintaining an internal reference count however (which would be comparable performance to this solution, but with more pain and maintenance), so comments and alternative solutions are welcome.

Advertisements
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

8 Responses to Virtual move constructor

  1. Nice writings!

    Can you propose an example of a use case for a polymorphic “buffer” type? IMO the C++ “default” idiom is for a type to be non-polymorphic unless there’s a specific need for virtual dispatch, but I can’t think of why a buffer would want it?

    Also moving the object everywhere seems a bit ew when you can just pass copies of a shared_ptr, or a reference, about instead?

    Dunno.

    • robdesbois says:

      Thanks 🙂

      So this code involves processing of a data buffer that can be injected into the program from multiple sources. Different sources require different mechanisms for managing buffer access (one contains a copy of the injected data, another lazy-loads the buffer on first access), and different merging semantics for adjacent buffers. We decided to handle these decisions through virtual dispatch.

      We only perform the move_clone once in this use case: when we want to extract the ownership from the shared_ptr (required for a copyable functor) back into a unique_ptr. Reference passing is a no-go since the functor must temporarily own the buffer.

      You’re right on shared_ptr though – a possibility would be to convert all of the unique_ptr instances to shared_ptr, though I shied away from it initially since the ref-counting has performance implications, and it also hides the intent to have single-ownership semantics. This may be me prematurely optimising though, so I may switch to shared_ptr universally instead.

  2. robdesbois :
    You’re right on shared_ptr though – a possibility would be to convert all of the unique_ptr instances to shared_ptr, though I shied away from it initially since the ref-counting has performance implications, and it also hides the intent to have single-ownership semantics. This may be me prematurely optimising though, so I may switch to shared_ptr universally instead.

    I guess it depends on what the move semantics are for the buffer. If you’re just swapping a single pointer then that’s gonna be faster than a ref count increase and decrease combined with shared_ptr instantiation, but that may not be all there is to it. And, as you say, it may be a PO.

    I’m biased against moves because I figure you can end up leaving zombie objects around the place if you don’t treat them consistently, but I suspect that’s more because I haven’t used move semantics all that much yet… I would agree that clearly demonstrating the intent for single-ownership semantics is a big plus here…

    • robdesbois says:

      I’ve found moving a rather useful feature. Much of my coding currently involves being given an object that is either non-copyable or is undesirable to copy, and transferring ownership of that into a container. Since a lot of the code that does this gets hit frequently under mutex lock, using shared_ptr is impractical, so unique_ptr and move is the way forward.

      I’ve not come across anything that I’d deem a zombie – generally if I’m moving from one object to another, it’s because the source is about to go out of scope, so it’s only a zombie for the remainder of that scope, if any.

  3. robdesbois :
    I’ve not come across anything that I’d deem a zombie – generally if I’m moving from one object to another, it’s because the source is about to go out of scope, so it’s only a zombie for the remainder of that scope, if any.

    *nods* When that’s the case, it’s fine! It’s just a “thing” for me that it’s not enforced by the language. Probably overreacting.

    As you know, C++11 isn’t something I get to play with during the day, and since that now includes most evenings (*sigh*) the internet-discussion-driven exposure has gone from my life too.

    So perhaps it’s just something I’d get used to given enough experience.

  4. Eric says:

    Wouldn’t it be just as effective, and more readable, to simply declare a pure virtual assignment operator?

    • robdesbois says:

      Eric – unfortunately that won’t work. The motivation is similar to that of the ‘virtual constructor’ idiom, aka ‘clone’: we don’t know exactly what type of object to create, since this is a polymorphic hierarchy and we know only the base class. This is why we must delegate the task of object creation to the derived type through virtual dispatch.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s