Search code examples
c++c++11inheritancec++17unique-ptr

About `std::unique_prt<Base>& pBase= make_unique<Derived>();`


The first piece of code does not compile, whereas the second one does. Why?

The code is almost the same indeed. I would be grateful to have some help with this question.

The code below does not compile. You could check it on http://cpp.sh/8j53y.

dynamic_cast
#include <iostream>
#include <memory>

using namespace std;

class Derived;

class Base { 
public:
    static unique_ptr<Base>& get_instance()
    {
        pBase = make_unique<Derived>();
        return pBase;
    }

private:
    static unique_ptr<Base> pBase;
};

class Derived: public Base { };

std::unique_ptr<Base> Base::pBase = nullptr;

int main () {

    auto& instance = Base::get_instance();
    return 0;
}

The code below does compile.

#include <iostream>
#include <memory>

using namespace std;

class Derived;

class Base { 
public:
    static unique_ptr<Base>& get_instance();

private:
    static unique_ptr<Base> pBase;
};

class Derived: public Base { };

std::unique_ptr<Base> Base::pBase = nullptr;

unique_ptr<Base>& Base::get_instance()
{
     pBase = make_unique<Derived>();
     return pBase;
}

int main () {
    auto& instance =  Base::get_instance();
    return 0;
}

Solution

  • When you define Base::get_instance inline, Derived doesn't have a definition yet (only the forward declaration). So, it can't convert std::unique_ptr<Derived> to std::unique_ptr<Base>, because the definition where Derived inherits from Base hasn't been seen yet. For the same reason, std::make_unique<Derived>() would also fail, because Derived doesn't have a definition yet.

    This is why you need to define Base::get_instance after the definition of Derived to compile. You can keep it in a header file by marking it inline then defining it out of line:

    class Base { 
    public:
        static unique_ptr<Base>& get_instance();
    
    private:
        static unique_ptr<Base> pBase;
    };
    
    class Derived: public Base { };
    
    /*
    `inline` here so the definition can appear in multiple translation units
    (e.g., directly in the header file)
    */
    inline unique_ptr<Base>& Base::get_instance()
    {
         pBase = make_unique<Derived>();
         return pBase;
    }