0

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;
}
smerlin
  • 6,446
  • 3
  • 35
  • 58
sunshilong369
  • 646
  • 1
  • 5
  • 17
  • 1
    I assume `auto Base::get_instance();` is meant to be `auto instance = Base::get_instance();` - Not that it will compile, but the problem won't be a typo related one – StoryTeller - Unslander Monica Jun 01 '20 at 14:01
  • 1
    It is very much the point. Links go stale, and I personally couldn't open your link to begin with. A question must be self contained. It must contain the correct code you ask about, without additional problems (and the error message too). You are asking *volunteers* to help you. Don't treat their time flippantly. – StoryTeller - Unslander Monica Jun 01 '20 at 14:23
  • Sorry for disturbing you.I would follow your advice.The web site is somewhere unstable today. – sunshilong369 Jun 01 '20 at 14:28
  • Aside: what's the point of holding your singleton in a `unique_ptr`? Do you really intend for callers to be able to reset that pointer? – Caleth Jun 01 '20 at 14:44
  • It is only a slice of a whole project.I partially agree with you.We asume that callers do not reset that pointer, then is it really unnecessary to use `unique_ptr`? – sunshilong369 Jun 01 '20 at 15:58
  • 'Base::get_instance' returns instance of 'Derived'? Is that meant so? Wouldn't be a factory pattern more appropriate? – Wör Du Schnaffzig Jun 01 '20 at 16:00
  • I admit that it is odd.And i would follow your advice to improve it. – sunshilong369 Jun 01 '20 at 16:12

1 Answers1

3

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;
}
Artyer
  • 31,034
  • 3
  • 47
  • 75
  • I'd move `inline` to the definition, though. `inline` exempts the _definition_ from the ODR, so putting it on the _declaration_, while valid, is misleading because it doesn't affect the declaration in any meaningful way. (It's an implementation detail that isn't part of the class interface.) – cdhowie Jun 01 '20 at 14:28
  • What's "ODR" short for ? – sunshilong369 Jun 01 '20 at 15:51
  • @Artyer I agree with you.But i still want to dig deep.You see,all the code could be seen by the compiler,so why doesn't it start to compile `std::unique_prt& pBase= make_unique();` after the whole file has been analyzed.I don't think it is so hard.What do you think? – sunshilong369 Jun 01 '20 at 16:04
  • @sunshilong369 See https://stackoverflow.com/q/19630570/5754656 for what the ODR is (in this case, there is an implicit `inline` when you write the function body in the class, which lets you write the same definition multiple times when it's `#include`d). The function needs to be instantiated as soon as it appears, not when the file has been analysed. What if you had something like `class Derived : public std::conditional_t {}`? Then you could only assign it if you couldn't assign it. That's one of the reasons why it is compiled when seen – Artyer Jun 01 '20 at 16:18
  • Thank you for your clarification. Sorry, I could not understand your example.Could you explain that in more detail?What do you mean by "Then you could only assign it if you couldn't assign it"? – sunshilong369 Jun 01 '20 at 16:30
  • @sunshilong369 In that example, what `Derived` inherits from could be conditional on whether `std::unique_ptr(std::make_unique())` is a valid expression, and in that example, if it was valid, it would inherit from a different class so it wouldn't be valid, and vice versa, making a paradox, so it is impossible to analyse "after the whole file has been analysed" (since you can make previous parts depend on future parts). In this case, it's actually UB to try and construct a unique_ptr from a unique_ptr to an incomplete class, which is how this is resolved. – Artyer Jun 01 '20 at 16:30
  • I think I understand your last reply to some degree.But I know I don't completly understand it.Thank you for your help.I would go out of my way to comprehend it.One more question, what do you mean by "UB".I could not find a proper meaning for it in the UrBan dictionary. – sunshilong369 Jun 01 '20 at 16:41