5

This code works fine:

class Test
{
    int* ptr = new int(10);
};

int main()
{       
     Test o;
     Test t = o;
}

But when we use unique_ptr instead raw ptr, we get an error:

error: use of deleted function 'Test::Test(const Test&)'

And sample code:

class Test
{
     std::unique_ptr<int> ptr = std::make_unique<int>(1);
};

int main()
{       
     Test o;
     Test t = o;
}

What is going on?

KeyB0rys
  • 392
  • 5
  • 13
  • 1
    Of course `unique_ptr` doesn't have a copy constructor, only a move constructor. If you want to copy `Test` use a `shared_ptr` (or do a deep copy as the linked answer suggests). – john Jan 22 '19 at 15:58
  • 2
    The hint is in the name. It wouldn't be a unique pointer if everyone can have one. – NathanOliver Jan 22 '19 at 15:59
  • 1
    In fact, the original code is _not_ fine. It has a memory leak, and switching to `std::unique_ptr` allows the compiler to prevent this (by not allowing your code). – Max Langhof Jan 22 '19 at 16:23

2 Answers2

8

What is going on?

You cannot create a second instance of Test because this implies you need a copy of the unique_ptr, which is not possbile. A unique_ptr can only be moved. Try implementing a move asignment operator and move o like so:

class Test
{
public:
    Test() = default;

    Test(Test&& other) noexcept
        : ptr(std::move(other.ptr))
    {
    }

private:
    std::unique_ptr<int> ptr = std::make_unique<int>(1);
};

int main()
{
    Test o;
    Test t = std::move(o);
}

If you want to copy the int underlying the unique_ptr, you need to define a custom copy constructor like this:

class Test
{
public:
    Test() = default;

    Test(const Test& other)
        : 
    ptr(new int(*other.ptr))
    {

    }

    Test(Test&& other) noexcept
        : ptr(std::move(other.ptr))
    {
    }

private:
    std::unique_ptr<int> ptr = std::make_unique<int>(1);
};

int main()
{
    Test o;
    Test t = o;
}

However, NOTE, that the pointers point to two DIFFERENT ints. If you want shared ownership, you have to (and should) use shared_ptr like this:

class Test
{
private:
    std::shared_ptr<int> ptr = std::make_shared<int>(1);
};

int main()
{
    Test o;
    Test t = o;
}
  • @user463035818 But that isn't a copy of the pointer. You now have two distinct pointers pointing to two distinct objects which is not the same thing as the OP's first example. – NathanOliver Jan 22 '19 at 16:03
  • @user463035818 It's possible that we're talking about different parts of the answer :P – Lightness Races in Orbit Jan 22 '19 at 16:04
  • @LightnessRacesinOrbit it is possible that I had to reload to see the edits... now I am fine with the answer. comments deleted – 463035818_is_not_an_ai Jan 22 '19 at 16:05
  • I would probably have implemented the copy constructor like this instead: `Test(const Test& other) : Test() { *ptr = *(other.ptr); }` or maybe `Test(int i = 0) : ptr(make_unique(i)) {} Test(const Test& other) : Test(*(other.ptr)) { }`. So that every instance of `Test` gets to use `make_unique` equally and there is a single point of allocation in the class, rather than having default constructed instances use `make_unique` while copied instances use `new`. But that is just me. Either way will "work". – Remy Lebeau Jan 22 '19 at 17:20
  • @RemyLebeau Good point. I probably would not even consider to implement it in this case either way as copying makes limited sense. –  Jan 22 '19 at 17:27
5

What is going on?

You've swapped from a naked pointer, to one that enforces ownership semantics. One that's smart.

This is literally the purpose of unique_ptr.

It enforces unique ownership.

You can't copy it; only move it.

If you really need Test to be copyable, and to be able to share ints, you're probably looking for shared_ptr.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Thank you for an answer. But where can I find a rule like: Delete copy constructor from class when the class has a unique_ptr member? – KeyB0rys Jan 22 '19 at 16:06
  • 1
    The compiler would have to invoke the copy constructor of the unique_ptr which is deleted. MSVC issues the following error `'Test::Test(const Test &)': attempting to reference a deleted function`. –  Jan 22 '19 at 16:10
  • 2
    @KeyB0rys See: https://timsong-cpp.github.io/cppwp/class.copy.ctor#10 – NathanOliver Jan 22 '19 at 16:11
  • @KeyB0rys As Nathan shows. Logically, how _could_ the class be copied when parts of it are defined as uncopyable? It makes no sense. – Lightness Races in Orbit Jan 22 '19 at 16:19