Copy-and-Move Concept Using Smart Pointer I tried to implement a simple container class using the copy-and-move idiom. I am learning the changes to C++ as of C++11 and later.


Is the code exception safe?
I chose to use a smart pointer. Is std::unique_ptr a safe choice? I read that it should not be copied, but since I am doing the copying of the content explicitly, and since it provides a swap function, I could not see any objection. Is there a preferred practice, considering the array content type might be any type including an object type?
When I let the compiler do the copy for the copy assignment operator (like ...::operator=(MyContainer other)) I got an ambiguity with the move assignment operator. So I chose to put the copy operation into the function (which I prefer visually, it might look naive and oldschool, but as an aging brain I find it more explicit). Also here: Is there another, preferred practice? And to overcome my personal preference: What is the real difference of letting the compiler do the copy? Both are taking place on the stack, aren't they?


Header part

class MyContainer
{
private:
    std::size_t mSize;
    std::unique_ptr&lt;int[]&gt; mArray;

    static void _swap(MyContainer&amp; first, MyContainer&amp; second);
    void _reset(void);

public:
    MyContainer(std::size_t size); // default constructor
    MyContainer(const MyContainer&amp; other); // copy constructor
    MyContainer(MyContainer&amp;&amp; other) noexcept; // move constructor
    MyContainer&amp; operator=(const MyContainer&amp; other); // copy assignment operator
    MyContainer&amp; operator=(MyContainer&amp;&amp; other) noexcept; // move assignment operator
    void dump(void);
    /* ... */
    ~MyContainer(void);
};


Source part

void MyContainer::_swap(MyContainer&amp; first, MyContainer&amp; second) // static
{
    std::swap(first.mSize, second.mSize);
    first.mArray.swap(second.mArray);
}

void MyContainer::_reset(void)
{
    mSize = 0;
    mArray.reset();
}

MyContainer::MyContainer(std::size_t size = 0) // default constructor
    : mSize(size),
    mArray(mSize ? new int[mSize] : nullptr)
{
    // for testing purpose only
    for (int i = 0; i &lt; mSize; i++)
        mArray.get()[i] = i;
}

MyContainer::MyContainer(const MyContainer&amp; other) // copy constructor
    : mSize(other.mSize),
    mArray(mSize ? new int[mSize] : nullptr)
{
    std::copy(other.mArray.get(), other.mArray.get() + mSize, mArray.get());
}

MyContainer::MyContainer(MyContainer&amp;&amp; other) noexcept // move constructor
    : MyContainer() // init using default constructor (C++11)
{
    _swap(*this, other);
}

MyContainer&amp; MyContainer::operator=(const MyContainer&amp; other) // copy assignment
{
    MyContainer tmp(other);
    _swap(*this, tmp);
    return *this;
}

MyContainer&amp; MyContainer::operator=(MyContainer&amp;&amp; other) noexcept // move assignment
{
    if (this != &amp;other)
    {
        _reset();
        _swap(*this, other);
    }
    return *this;
}


void MyContainer::dump(void)
{
    std::cout &lt;&lt; "Size: " &lt;&lt; mSize &lt;&lt; std::endl;
    for (int i = 0; i &lt; mSize; i++)
        std::cout &lt;&lt; mArray.get()[i] &lt;&lt; std::endl;
}

MyContainer::~MyContainer(void)
{
}
unique_ptr is fine here. It's used where you would have used a plain pointer before.

But don't use naked new — use make_unique.  Oh, and I think a unique_ptr of array types was not in C++11 but needs C++14.

Don’t use (void) for () in parameter lists!  That was never proper, even in c++98. (it was supported to allow compatibility with C headers, but in Bjarne’s own words it’s “an abomination”.

Don’t write an empty destructor.  Use =default to emphasize that it exists, or just leave it off completely.

Why have a private _swap static function instead of a non-member overload of swap that anyone can use?

Another important new feature you have not embraced yet is the range for loop.  Use that instead of the lagacy for loops you have that go over the whole array.