1

I have a hierarchie of classes that implement the Prototype pattern and I would like to use move semantics to limit objects deep copy. I tried to adapted the pattern with a move() member function which meaning is that I do not need the original object anymore. Here is what I have so far:

#include <iostream>
#include <utility>
#include <vector>

struct base
{
    virtual ~base() { }
    virtual base* clone() const = 0;
    virtual base* move() = 0;
};

struct derived1 : public base
{
    derived1()                { std::cout << "derived1::derived1()\n"; }
    derived1(const derived1&) { std::cout << "derived1::derived1(const derived1&)\n"; }
    derived1(derived1&&)      { std::cout << "derived1::derived1(derived1&&)\n"; }

    virtual ~derived1() { }

    virtual base* clone() const { return new derived1(*this); }
    virtual base* move()        { return new derived1(std::move(*this)); }
};

struct derived2 : public base
{
    derived2()                { std::cout << "derived2::derived2()\n"; }
    derived2(const derived2&) { std::cout << "derived2::derived2(const derived2&)\n"; }
    derived2(derived2&&)      { std::cout << "derived2::derived2(derived2&&)\n"; }

    virtual ~derived2() { }

    virtual base* clone() const { return new derived2(*this); }
    virtual base* move()        { return new derived2(std::move(*this)); }
};

std::vector<base*> vec;

void foo(const base& obj)
{
    vec.push_back(obj.clone());
}

void foo(base&& obj)
{
    vec.push_back(obj.move());
}

int main()
{
    derived1 d1;
    derived2 d2;

    foo(d1);
    foo(d2);
    foo(derived1());
    foo(derived2());
}

When I run it, it show that the good constructors are used:

derived1::derived1()
derived2::derived2()
derived1::derived1(const derived1&)
derived2::derived2(const derived2&)
derived1::derived1()
derived1::derived1(derived1&&)
derived2::derived2()
derived2::derived2(derived2&&)

So far, it seems good. I am just not sure if this is a standard compliant usage of the rvalue references. Is there a point I did not think of that would produce undesirable results?

authchir
  • 1,605
  • 14
  • 26
  • 3
    Sounds like a job for [function ref-qualifiers](http://stackoverflow.com/a/8610728/46642). So far only available in clang, I think. – R. Martinho Fernandes Jul 15 '12 at 21:58
  • @R.MartinhoFernandes This look like an interesting feature, but I don't get what you mean. How should it be use in my situation? – authchir Jul 15 '12 at 23:22
  • 5
    you can overload `clone()` for lvalues and for rvalues instead of using a function with a different name. – R. Martinho Fernandes Jul 15 '12 at 23:24
  • @R.MartinhoFernandes Well, it would change the second `foo` function to call `std::move(obj).clone()` instead of `obj.move()`. Maybe it's more elegent, but it looks like just a syntaxic detail. I imagine that, if you raise this kind of observation, it's because the core idea is suppose to work and is well defined according to the standard? – authchir Jul 16 '12 at 00:18
  • 1
    If you're passing these things around by pointer, why would you ever need this move function? And if you're not passing them by pointer, how are these things being passed around and still being polymorphic types? – Nicol Bolas Jul 16 '12 at 00:25
  • 3
    Yes, it works fine. I just mentioned the ref-qualifiers because they're more generic: you can make things like `std::forward(obj).clone()` work effortlessly, as you don't need to care if `obj` is forwarded as an lvalue or rvalue reference to pick the cloning function. – R. Martinho Fernandes Jul 16 '12 at 00:25
  • @R.MartinhoFernandes +1 Thanks, I did not think of this generic use. If you write this in an answer, I will upvote and accept it. – authchir Jul 16 '12 at 00:27

1 Answers1

1

For recurring method definition I prefer CRTP. For your case I'd declare something like:

template<typename TDerived>
class virtually_clonable : public base
{
public:
    virtual base* clone() override
    {
        return new TDerived(*AsDerived());
    }

    virtual base* move() override
    {
        return new TDerived(std::move(*AsDerived()));
    }
private:
    TDerived* AsDerived()
    {
        return static_cast<TDerived*>(this);
    }
};

And while implementing the classes:

class derived1 : public virtually_clonable<derived1>
{
public:
/* your ctors goes here*/
/* no need to implement clone/move again */
};

class derived2 : public virtually_clonable<derived2>
{
public:
/* your ctors goes here*/
/* no need to implement clone/move again */
};

By the way you may want to return shared_ptr objects instead of raw pointers. That is usually the case for clonable types instead of unique_ptr.

Sriram Murali
  • 5,804
  • 2
  • 26
  • 32
zahir
  • 1,298
  • 2
  • 15
  • 35