Home > Net >  How to call all native constructors (rule of five)
How to call all native constructors (rule of five)

Time:05-25

I wrote my own Matrix class. To test its constructors and operators, I need to call each one. How can i do this? Now called move operator and copy constructor, but still need move constructor and assignment operator

 Matrix(const Matrix &other) : data(new T[other.rows * other.cols]), rows(other.rows), cols(other.cols)
        {
            std::cout << "copy constructor" << std::endl;
            for (size_t i = 0; i < rows * cols;   i)
            {
                data[i] = other.data[i];
            }
        }

        Matrix &operator=(const Matrix<T> &other)
        {
            std::cout << "assignment operator" << std::endl;
            if (&other == this)
            {
                return *this;
            }

            if (rows != other.rows || cols != other.cols)
            {
                throw std::logic_error("Error assigning matrices of different sizes");
            }

            data = (static_cast<T *>(operator new[](sizeof(T) * rows * cols, static_cast<std::align_val_t>(alignof(T)))));
            rows = other.rows;
            cols = other.cols;

            for (size_t i = 0; i < rows * cols;   i)
            {
                data[i] = other.data[i];
            }

            return *this;
        }

        Matrix(Matrix &&other) noexcept : data(other.data), rows(other.rows), cols(other.cols)
        {
            std::cout << "Move constructor" << std::endl;
            other.data = nullptr;
            other.rows = 0;
            other.cols = 0;
        }

        Matrix &operator=(Matrix<T> &&other)
        {
            std::cout << "move operator" << std::endl;
            if (&other == this)
            {
                return *this;
            }

            if (rows != other.rows || cols != other.cols)
            {
                throw std::logic_error("Error assigning matrices of different sizes");
            }

            clean();

            std::swap(data, other.data);
            std::swap(rows, other.rows);
            std::swap(cols, other.cols);

            other.data = nullptr;
            other.rows = 0;
            other.cols = 0;

            return *this;
        }

Here are the constructor calls, how to call all the available constructors in general? main.cpp

Matrix<int> mat1(3, 3);
Matrix<int> mat2(3, 3);
mat1.fillMatrix();
mat2.fillMatrix();

mat2 = std::move(mat1);

Matrix<int> mat3 = mat1;
mat3.printMatrix();
Matrix<int> mat4(mat1);

CodePudding user response:

You can use casts and std::move here:

Matrix<int> source;
Matrix<int> target(std::move(source)); // move constructor
Matrix<int> source;
Matrix<int> target;
target = std::move(source); // move assignment operator
Matrix<int> source;
Matrix<int> target(static_cast<Matrix<int> const&>(source)); // copy constructor
Matrix<int> source;
Matrix<int> target;
target = static_cast<Matrix<int> const&>(source); // copy assignment

Note that technically the casts to const references are unnecessary, but with the cast you would also be able to prevent another overload taking Matrix<int>& as parameter from being applicable.

Note: If you write unit tests, it may be a good idea to add static_asserts checking for the existance of the special member functions.

static_assert(std::is_move_constructible_v<Matrix<int>>, "Matrix<int> is not move constructible");
static_assert(std::is_move_assignable_v<Matrix<int>>, "Matrix<int> is not move assignable");
static_assert(std::is_copy_constructible_v<Matrix<int>>, "Matrix<int> is not copy constructible");
static_assert(std::is_copy_assignable_v<Matrix<int>>, "Matrix<int> is not copy assignable");

This way you'd get a compiler error, if you got one of the signatures wrong...

CodePudding user response:

Matrix<int> mat1(3, 3);                           // not shown constructor
Matrix<int> mat2(mat1);                           // copy constructor
Matrix<int> mat3 = mat1;                          // copy constructor
mat3 = mat1;                                      // operator=(const Matrix&)
Matrix<int> mat4(std::move(Matrix<int>(3, 3)));   // move constructor
Matrix<int> mat5 = std::move(Matrix<int>(3, 3));  // move constructor
mat5 = Matrix<int>(3, 3);                         // operator=(Matrix&&)

https://godbolt.org/z/eoodeoK6h


Explicit std::move is used for avoiding elisions.

CodePudding user response:

Copy and move constructors are called when you initializing new object. Difference between move and copy is what argument you use to initialize it. If it tempory or special std::move function is used than move ctor would be called(not in all cases though), otherwise copy constructor. For example:

Matrix<int> mat1(3,3);
Matrix<int> mat3 = mat1; //cp-ctor
Matrix<int> mat4 = std::move(mat1);//mv-ctor
Matrix<int> mat5(mat1);//cp-ctor full equivalent of second line
Matrix<int> mat6 = someFunctionReturningMatrixByCopy(); //mv-ctor
  •  Tags:  
  • c
  • Related