vendredi 4 décembre 2015

Both move assignment and move constructor are emitted from a function call

I am new to C++11 and found move semantics and copy ellision are really great to write elegant and efficient code. However I have some problems would like to ask. Here I write a template class matrix.hpp and use it to test behaviors of move semantics.

#include <vector>
#include <iostream>

template<class T> class matrix {
public:
    matrix(); // default constructor
    matrix(const matrix<T>& mx); // copy constructor
    matrix(matrix<T>&& mx); // move constructor
    matrix(int rows_, int cols_);

    matrix<T>& operator= (matrix<T>&& mx); // move assignment
    matrix<T>& operator= (const matrix<T>& mx); // copy constructor

   matrix<T> mean(int axis);
private:
    int rows, cols;
    std::vector<T> data;
};
template<class T> matrix<T>::matrix(): rows(0), cols(0), data(0) {}
template<class T> matrix<T>::matrix (int rows_, int cols_)
    : rows(rows_), cols(cols_), data(rows * cols) {}
template<class T> matrix<T>::matrix(const matrix<T>& mx) {
    cout << "copy-tor" << endl;
    rows = mx.rows;
    cols = mx.cols;
    data = mx.data;
}
template<class T> matrix<T>::matrix(matrix<T>&& mx) {
    cout << "move-tor" << endl;
    rows = mx.rows;
    cols = mx.cols;
    data = std::move(mx.data);
}
template<class T> matrix<T>& matrix<T>::operator= (const matrix<T>& mx) {
    cout << "copy-assign" << endl;
    if (this != &mx) {
        data.clear();
        cols = mx.cols;
        rows = mx.rows;
        data = mx.data;
    }
    return *this;
}
template<class T> matrix<T>& matrix<T>::operator= (matrix<T>&& mx) {
    cout << "move-asign" << endl;
    if (this != &mx) {
        data.clear();
        rows = mx.rows;
        cols = mx.cols;
        data = std::move(mx.data);
    }
    return *this;
}
template<class T> matrix<T> matrix<T>::mean(int axis) {
    matrix<T> mx(axis == 0 ? 1 : rows, axis == 1 ? cols : 1);
    // HERE compute mean vector ...
    return mx;
}

And in the test.cpp I test how copy constructors and move semantics are implemented in the following cases:

#include "matrix.hpp"
matrix<float> f() {
    matrix<float> a(1,2);
    return a;
}
matrix<float> g() {
    matrix<float> *b = new matrix<float>(1,2);
    return *b;
}
int main() {
    matrix<float> a;
    a = f(); // (*)
    cout << "--" << endl;
    a = g(); // (**)
    cout << "--" << endl;
    a = a.mean(1);  // (***)
}

The result is:

move-asign
--
copy-tor
move-asign
--
move-tor
move-asign

The first result is straightforwardly inferred from the definition of move assigment. My guess for the second result is that the compiler will create a temporary of the object *b, and then std::move() this temporary object to a. For the third result I have no cue. Could anyone explain it to me? Thanks!

Aucun commentaire:

Enregistrer un commentaire