c++option-typeunique-ptrnamed-constructor

Combining std::unique_ptr and named constructors


So I am pretty new to C++ and I'm trying to combine std::unique_ptr with a named constructor returning a std::optional. I've got the following structure:

class AbstractClass {
public:
    virtual ~AbstractClass() {}
};

class ChildClassA : public AbstractClass {
public:
    static std::optional<ChildClassA> construct(...) { ... }

private:
    ChildClassA(...) : ...{...} { ... }
};

std::unique_ptr<AbstractClass> construct(...) {
    if (...) {
        return std::make_unique<ChildClassA>(...); // call ChildClassA::construct(...) here
    } else {
        return std::make_unique<ChildClassB>(...); // call ChildClassB::construct(...) here
    }
}

I want to have a function construct() that calls the constructor of one of the child classes depending on some value. The constructors of these child classes may fail, thus I am using named constructors returning an std::optional as described here. construct() should return a std::unique_ptr to make passing of ownership explicit and prevent copying of the constructed object.

Is this possible?


Solution

  • If your classes are movable then you can move them into the unique_ptr:

    #include <optional>
    #include <memory>
    
    class AbstractClass {
    public:
        virtual ~AbstractClass() {}
    };
    
    class ChildClassA : public AbstractClass {
    public:
        static std::optional<ChildClassA> construct();
    
    private:
        ChildClassA(){}
    };
    
    class ChildClassB : public AbstractClass {
    public:
        static std::optional<ChildClassB> construct();
    
    private:
        ChildClassB(){}
    };
    
    std::unique_ptr<AbstractClass> construct(bool a) {
        if (a) {
            auto temp = ChildClassA::construct();
            if (temp) {
                return std::make_unique<ChildClassA>(std::move(*temp));
            }
            return {};
        } else {
            auto temp = ChildClassB::construct();
            if (temp) {
                return std::make_unique<ChildClassB>(std::move(*temp));
            }
            return {};
        }
    }
    

    However a possibly null unique_ptr would be much simpler for this usecase:

    #include <optional>
    #include <memory>
    
    class AbstractClass {
    public:
        virtual ~AbstractClass() {}
    };
    
    class ChildClassA : public AbstractClass {
    public:
        static std::unique_ptr<ChildClassA> construct();
    
    private:
        ChildClassA(){}
    };
    
    class ChildClassB : public AbstractClass {
    public:
        static std::unique_ptr<ChildClassB> construct();
    
    private:
        ChildClassB(){}
    };
    
    std::unique_ptr<AbstractClass> construct(bool a) {
        if (a) {
            return ChildClassA::construct();
        } else {
            return ChildClassB::construct();
        }
    }