Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does returning std::make_unique<SubClass> work?

I have a base class and its subclass:

class Base {
    public:
    virtual void hi() {
        cout << "hi" << endl;
    } 
};

class Derived : public Base {
    public:
    void hi() override {
        cout << "derived hi" << endl;
    } 
};

Trying to create a helper function that creates a unique pointer of a Derived object.

1) This one works:

std::unique_ptr<Base> GetDerived() {
    return std::make_unique<Derived>(); 
}

2) But, this one fails to compile:

std::unique_ptr<Base> GetDerived2() { 
    auto a = std::make_unique<Derived>(); 
    return a; 
}

3) std::move works:

std::unique_ptr<Base> GetDerived3() {
    auto a = std::make_unique<Derived>();
    return std::move(a); 
}

4) If I create a Base instance, both work:

std::unique_ptr<Base> GetDerived4() {
    auto a = std::make_unique<Base>();
    return a; 
}

std::unique_ptr<Base> GetDerived5() {
    auto a = std::make_unique<Base>();
    return std::move(a); 
}

Why (2) fails but others work?

like image 334
MaxHeap Avatar asked Sep 13 '16 21:09

MaxHeap


People also ask

What does make_unique return C++?

template<class U> unique_ptr<U> make_unique(U&& value); Returns: A unique_ptr to an object of type U , initialized to move(value) .

Does make_unique call new?

make_unique teaches users "never say new / delete and new[] / delete[] " without disclaimers. make_unique shares two advantages with make_shared (excluding the third advantage, increased efficiency).

Why should I use make_unique?

make_unique is safe for creating temporaries, whereas with explicit use of new you have to remember the rule about not using unnamed temporaries.

Can unique_ptr be null?

Nullability - a scoped_ptr or unique_ptr can be null, a value object can never be. Polymorphism - a value object is always exactly its static type, but you can substitute in different derived types for a unique_ptr. The previously-held object is automatically destroyed when you do this.


1 Answers

std::unique_ptr is not copyable, only movable. The reason you can return std::make_unique<Derived> from a function declared to return std::unique_ptr<Base> is that there is a conversion from one to the other.

So 1) is equivalent to:

std::unique_ptr<Base> GetDerived() {
    return std::unique_ptr<Base>(std::make_unique<Derived>());
}

Since the value returned from std::make_unique is an rvalue, the return value is move-constructed.

Contrast that to 2), which is equivalent to:

std::unique_ptr<Base> GetDerived2() { 
    std::unique_ptr<Derived> a = std::make_unique<Derived>(); 
    return std::unique_ptr<Base>(a); 
}

Since a is an lvalue, the return value must be copy-constructed, and std::unique_ptr is non-copyable.

  1. works because you cast the lvalue a to an rvalue, and the return value can be move-constructed.

  2. and 5) work because you already have a std::unique_ptr<Base> and do not need to construct one to return.

like image 155
Miles Budnek Avatar answered Sep 28 '22 20:09

Miles Budnek