By Calmarius


2019-03-09 10:36:45 8 Comments

The typical pattern when you want to copy a polymorphic class is adding a virtual clone method and implement it in each derived class like this:

Base* Derived::clone()
{
    return new Derived(*this);
}

Then in a calling code you can:

Base *x = new Derived();
Base *y = x->clone();

However if you have 50+ derived classes and realize you need polymorphic copy, it's tedious to copy-paste the cloning method into each of them. And it's essentially a boilerplate that works around a language limitation that you have to spell out the actual name to call the constructor.

I haven't keep track with the new features in recent C++ standards... Is there a way to avoid this in modern C++?

6 comments

@Klaus 2019-03-09 10:50:53

You can at minimum avoid writing the class name by getting it from the type of the class itself with:

struct A: public Base
{
    Base* Clone() { return new std::remove_reference_t<decltype(*this)>(*this); }
};

Using CRTP will not help avoid duplicating the class name again, as you than have to write the class name inside the template parms for the CRTP base.

@Justin 2019-03-10 02:15:43

If you can control how you pass around the polymorphic type, use type erasure. In particular, the proposed std::polymorphic_value calls the derived copy constructor when it is copied. You can imagine it as something like this:

template <typename B>
class polymorphic_value {
public:
    template <typename D,
        std::enable_if_t<
            std::is_base_of<B, std::decay_t<D>>::value, int> = 0>
    explicit polymorphic_value(D&& value)
        : ptr{std::make_unique<derived_t<std::decay_t<D>>>(std::forward<D>(value))}
    {}

    polymorphic_value(polymorphic_value const& rhs)
        : ptr{rhs.ptr->clone()}
    {}

    B const& get() const { return ptr->get(); }

    B& get() {
        // Safe usage of const_cast, since the actual object is not const:
        return const_cast<B&>(ptr->get());
    }

private:
    struct base_t {
        virtual ~base_t() = default;
        virtual std::unique_ptr<B> clone() const = 0;
        // With more effort, this doesn't have to be a virtual function.
        // For example, rolling our own vtables would make that possible.
        virtual B const& get() const = 0;
    };

    template <typename D>
    struct derived_t final : public base_t {
        explicit derived_t(D const& d)
            : value{d}
        {}

        explicit derived_t(D&& d)
            : value{std::move(d)}
        {}

        std::unique_ptr<B> clone() const override {
            return std::make_unique<D>(value);
        }

        B const& get() const override {
            return value;
        }

        D value;
    };

    std::unique_ptr<base_t> ptr;
};

For a thorough implementation which follows the proposal, see jbcoe's github repository.

Sample usage:

class Base {
public:
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    Derived() = default;
    Derived(Derived const&);
};

int main() {
    polymorphic_value<Base> it{Derived{}};
    auto const copy = it;
}

Live on Godbolt

@n.m. 2019-03-09 11:10:53

You can use this generic CRTP code

template <class Derived, class Base>
struct Clonable : Base {
    virtual Base* do_clone() {
        return new Derived(*static_cast<Derived*>(this));
    }
    Derived* clone() { // not virtual
        return static_cast<Derived*>(do_clone());
    }

    using Base::Base;
};

struct empty {};
struct A : Clonable<A, empty> {};
struct B : Clonable<B, A> {};

It can be generalised to smart pointers and multiple bases if desired.

@Calmarius 2019-03-09 12:09:40

Wow, I have never heard of this pattern. I will check if I can use this for my problem.

@Red.Wave 2019-03-09 16:58:16

@Calmarius CRTP is a mostly c++ pattern. No wonder if you are used to other languages.

@Kevin 2019-03-10 01:32:47

@Red.Wave: Outside of C++, it's called "F-bounded polymorphism." You will mostly hear about this in the context of Java's Comparable<T> interface. Because C++ templates are fundamentally different from type erasure, the CRTP and F-bounded polymorphism are obviously not exactly the same thing, but in practice they tend to solve the same sorts of problems.

@Red.Wave 2019-03-10 02:31:15

@Kevin didn't think it was that common outside C++.👍🏻

@Eric 2019-03-10 04:29:44

What's the purpose of the do_clone() method returning Base? πάντα ῥεῖ's answer seems a lot more obviously correct to me

@n.m. 2019-03-10 04:35:18

@Eric in this scheme one cannot have a virtual clone function returning Derived*. So it is split into the virtual part (do_clone) and the one with the correct return type (clone). If you're OK with returning Base* then this split is not needed.

@Eric 2019-03-10 04:38:14

@n.m.: "one cannot have a virtual clone function returning Derived*" - is that not exactly what the answer below does?

@n.m. 2019-03-10 04:42:15

@Eric which one? Cannot seem to find any that does.

@Eric 2019-03-10 04:47:13

Huh, you're right, my mistake. This question I think answers my question - covariant return types and CRTP don't mix.

@Kilian 2019-03-09 16:44:02

You probably have a class where you store the polymorphic object and where you want to clone? Together with your polymorphic object you could store a function-pointer doing the cloning:

template<class Derived>
Base* clone(const Base* b) {
    return new Derived(static_cast<const Derived*>(b));
}

void SampleUsage() {
    Base* b = new Derived;
    Base*(*cloner)(const Base*) = clone<Derived>;
    Base* copy = cloner(b);
}

The type of cloner is independent of Derived. Its like a simplified std::function.

@Justin 2019-03-10 04:44:45

This is actually a good idea, and it's the basis behind type erasure. To improve usability, it's a good idea to wrap the Base* and the Base*(*)(const Base*) function pointer together in a class that automatically does this correctly.

@πάντα ῥεῖ 2019-03-09 10:47:51

You could use a CRTP approach, but that has other drawbacks:

struct Base {
    virtual Base* clone() const = 0;
};

template <typename Derived>
class BaseT : public Base {
    // ...
public:
    Base* clone() const override {
        return new Derived(*static_cast<Derived*>(this));
    }
};

Usage:

class DerivedA : public BaseT<DerivedA> {
};

Base *x = new DerivedA();
Base *y = x->clone();

I haven't keep track with the new features in recent C++ standards... Is there a way to avoid this in modern C++?

This trick is available since the c++98 standard.

@Peter 2019-03-09 13:38:42

This trick is available since C++98 IF one omits the override specifier (which was not introduced until C++11).

@Eric 2019-03-10 04:30:37

Cloneable would probably be a better name than Base

@super 2019-03-09 10:48:59

You could use CRTP to add an additional layer between your derived class and base that implements your cloning method.

struct Base {
    virtual ~Base() = default;
    virtual Base* clone() = 0;
};

template <typename T>
struct Base_with_clone : Base {
    Base* clone() {
        return new T(*this);
    }
};

struct Derived : Base_with_clone<Derived> {};

@Klaus 2019-03-09 10:52:18

which shifts the naming of the class from the "new" to the CRTP base class... Did it help?

@Klaus 2019-03-09 10:55:32

You don't have to write "new ClassName"... but instead you write class Derived<CRTP_BASE<Derived>> So you simply shift the need of writing the name of the class from the "new" statement to the CRTP one. Helpful? ;) I mention that as OP mention that in his question that he want to avoid naming the derived class name in every new derived class type.

@super 2019-03-09 10:57:15

@Klaus Arguably adding <Derived> is a lot less typing then adding a whole cloning method in the class. Indeed a matter of taste, but I would say it could be very helpful.

@Klaus 2019-03-09 10:58:29

Agree! It goes only to the point that OP can't avoid with CRTP to give the name of the derived class again. Only for that, because it was especially mention by OP.

@super 2019-03-09 11:00:19

@Klaus It's not especially mentioned, the only thing mentioned by OP is the fact that he finds it tedious to add a clone method in each derived class. This offers the alternative.

@Klaus 2019-03-09 11:01:01

I read ` that you have to spell out the actual name to call the constructor.` in the question ;)

@super 2019-03-09 11:06:30

@Klaus So you choose to interpret that very freely as "I want a solution that does not have to repeat the name of the derived class in any way", and pass on this constructive information in the comments to me. Seems like your input in not helping to improve the quality of the answer in the least.

Related Questions

Sponsored Content

24 Answered Questions

[SOLVED] Image Processing: Algorithm Improvement for 'Coca-Cola Can' Recognition

4 Answered Questions

4 Answered Questions

4 Answered Questions

[SOLVED] Calling base class virtual method by derived class virtual method

  • 2011-10-04 06:44:49
  • Sanish
  • 14045 View
  • 6 Score
  • 4 Answer
  • Tags:   c++

6 Answered Questions

2 Answered Questions

[SOLVED] C++ elegantly clone derived class by calling base class

2 Answered Questions

[SOLVED] c++ polymorphism, name resolution for derived class

2 Answered Questions

[SOLVED] C++ Copying objects with polymorphism

2 Answered Questions

[SOLVED] How to avoid clone() boilerplate code for polymorphic objects in c++

1 Answered Questions

[SOLVED] Method of derived class needs to downcast its parameter

  • 2010-01-30 15:56:29
  • Ivan Virabyan
  • 554 View
  • 1 Score
  • 1 Answer
  • Tags:   c++

Sponsored Content