1
#include <iostream>
#include<algorithm>

template<class T>
class Matrix {
    std::pair<unsigned int,unsigned int> dim;
    T* elem;
public:
    Matrix(unsigned int d1, unsigned int d2) :
        dim{std::make_pair(d1,d2)}, elem{new T[d1*d2]} { }

    unsigned int size() const { return (dim.first)*(dim.second); }

    Matrix(Matrix&& a){
        std::cout<<"move constructor";
        elem = a.elem;
        a.elem =nullptr;
        dim.first = a.dim.first+7;
        dim.second = a.dim.second;
        a.dim.first=0;
        a.dim.second=0;
    }

    Matrix& operator=(Matrix&& a){
        std::cout<<"move operator=";
        elem = a.elem;
        a.elem =nullptr;
        dim.first = a.dim.first;
        dim.second = a.dim.second;
        a.dim.first=0;
        a.dim.second=0;
        return *this;
    }

    ~Matrix() { delete[] elem; }
};

using namespace std;

int main() {
    Matrix<unsigned int> bob = Matrix<unsigned int>(5,5);
    Matrix<unsigned int> bob2(Matrix<unsigned int>(5,5));
    return 0;
}//no output

I'm expecting it to print "move constructor" and "move operator=" But it prints neither of them.

Matrix(5,5) doesnt have a name so i assume its rvalue, thus im expecting Matrix<unsigned int> bob = Matrix<unsigned int>(5,5); to call the move constructor

ANjaNA
  • 1,404
  • 2
  • 16
  • 29
  • 2
    Why not strip out all the unnecessary details from your example code? – John Zwinck Jul 25 '18 at 14:33
  • 6
    Research "copy elision". – Jesper Juhl Jul 25 '18 at 14:36
  • found that this is a duplicate of: https://stackoverflow.com/questions/13099603/c11-move-constructor-not-called-default-constructor-preferred – Eric Freeman Jul 25 '18 at 14:54
  • 1
    Your code does not use `operator =` anywhere. In its current form there's no chance for *any* `operator =` to get called, move or non-move. – AnT stands with Russia Jul 25 '18 at 15:10
  • everything worked as expected after adding -fno-elide-constructors tag to disable copy elision – Eric Freeman Jul 25 '18 at 15:12
  • It is worth noting that by defining these "move" members you automatically made the compiler to implicitly *delete* the "copy" versions of these members. So, if your "move" versions are not called, it means that "copy" versions are not called either (they don't exist). I.e. it should have been clear from the very beginning that *nothing* is called here. All copying has been elided. – AnT stands with Russia Jul 25 '18 at 15:18
  • (Reference for what @AnT said: https://stackoverflow.com/a/4944131/560648) – Lightness Races in Orbit Jul 25 '18 at 15:19

1 Answers1

6

This is a feature.

Moves are better than copies, but C++ in this case is allowed to skip even the move! It's called elision and is most noticeable when you initialise like this, or return from a function. In fact since C++17 I think it's even guaranteed; in the olden days it was just a permitted optimisation. Note that it's permitted even if the constructor has side effects (e.g. output), which is quite unusual for C++.

But there is nothing wrong with your code, which is indeed correctly poised to use the move constructor. The code would not compile otherwise, as elision is only permitted when the move could have been performed.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055