Is it necessary to implement move constructor for a class without dynamic resources?

Viewed 306

I have a container class like the following. As you can see that all the resources that the class use is allocated statically. There are no dynamically allocated resources in the class. Does such a class need a move constructor or move assignment operator?

template<class T, std::size_t SIZE>
class Stack{
    static_assert(SIZE != 0, "Stack capacity cannot be zero!");

public:
    /*** Constructors and Destructor ***/
    Stack() = default;             // Default constructor
    Stack(const Stack& copyStack); // Copy constructor
    ~Stack();                      // Destructor

    /*** Member Methods ***/
    /* .... */
    void swap(Stack& swapStack);

private:
    /*** Members ***/
    std::size_t idxTop{0};   // Index after the top element
    T data[SIZE];            // Contained data
};

For the ones who would like to try it out with the actual implementation:

3 Answers

What exactly do you mean by need? I guess that you mean whether you need to write them for you class to stay moveable.

If you have an example like this:

#include <cstdint>
#include <utility>
class Foo
{
  private: 
  std::size_t idxTop{0};
  int data[4];
};

int main()
{
  Foo f = Foo(); // Default ctor
  Foo x = Foo(f); // Copy ctor
  x = f; // Copy assignment
  x = std::move(f); // Move assignment
  Foo y = std::move(f); // Move ctor
}

The compiler will add

  inline constexpr Foo() noexcept = default;
  inline constexpr Foo(const Foo &) noexcept = default;
  inline constexpr Foo(Foo &&) noexcept = default;
  inline constexpr Foo & operator=(const Foo &) noexcept = default;
  inline constexpr Foo & operator=(Foo &&) noexcept = default;

to the class's definition. I.e the class can be copied, moved freely as is shown in the main.

But - that's maybe what prompted your question - if you write a custom {copy con,des}structor for a class as you have done, the compiler will not put the defaulted definitions there. Why? Because by writing custom ones, you sent a signal that your class in some way special and default implementations would most likely not do the right thing.

So, your class is not moveable but remains copyable and copies will be used instead of move operations when required - e.g. the last two lines in my example.

I would suggest that whenever you customize one of those 5 functions, you define all of them, use default or delete if they suffice.

You can find more information about the default rules in the rule of three/five/zero.

Here's the thing. Your class isn't movable, because it doesn't have dynamically allocated resources. But the resources it contains might. A T, for instance, might be a std::vector in some instantiation. That can surely be moved, so you have to make sure that by providing a copy constructor (you forgot to = default; it btw), the compiler doesn't accidentally disable the default move constructor.

Now, you should be careful about the default move constructor in your case. The problem is that you have an array as a member, and the default move constructor just moves the elements of the array one by one.

Except what happens when the move constructor of one of them throws? You end up with half of the elements moved, and some still in the old array because an exception was thrown. That's no good. Take a look at std::move_if_noexcept and perhaps try implementing the move constructor to fix this.

No, your class does not need a move-ctor or move-assignment-operator.

You can't move static arrays, you can only copy their content. So a potential move-ctor would do the same as the copy-ctor. The same goes for assignment operators.

Related