Search code examples
c++abstract-classbase-class

Copy-constructing data members of abstract type


Consider the following scenario:

class AbsBase {
public:
    virtual double foo() = 0;
};

class Concrete1 : public AbsBase {
public:
    double foo() {
        return 1;
    }
};

class Concrete2 : public AbsBase {
public:
    double foo() {
        return 2;
    }
};

struct Config {
    /**
     * Must use a pointer to `AbsBase`, since it is an abstract base class, 
     * and the concrete instances could be of either of the derived classes.
     */
    AbsBase* thing;

    void setThing(AbsBase* t) {
        thing = t;
    }
};

This works, but I would prefer it if thing were a copy of the argument passed to setThing. I.e. if thing were of type Concrete1 (as opposed to an abstract base class) I could do something like this:

Concrete1 thing;
void setThing(const Concrete1& t) {
    thing = Concrete1(t);
}

However I can't see a way of achieving this without instantiating an object of an abstract type (which is illegal). Any ideas?


Solution

  • You can use a template function to achieve the wanted behavior. I would recommend to also use std::unique_ptr to prevent memory issues.

    class AbsBase {
    public:
        virtual ~AbsBase() = default;
        virtual double foo() = 0;
    };
    
    class Concrete1 : public AbsBase {
    public:
        double foo() {
            return 1;
        }
    };
    
    class Concrete2 : public AbsBase {
    public:
        double foo() {
            return 2;
        }
    };
    
    struct Config {
        /**
         * Must use a pointer to `AbsBase`, since it is an abstract base class, 
         * and the concrete instances could be of either of the derived classes.
         */
        std::unique_ptr<AbsBase> thing;
    
        template <typename T>
        void setThing(const T& t) {
            static_assert(std::is_base_of<AbsBase, T>::value, "T must inherit AbsBase");
            static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
            thing.reset(new T{t});
        }
    
        void test()
        {
            std::cout << (thing?thing->foo():0.0) << std::endl;
        }
    };
    

    https://gcc.godbolt.org/z/fID5UM