C++ 移动赋值和移动构造函数都是从函数调用发出的

C++ 移动赋值和移动构造函数都是从函数调用发出的,c++,c++11,copy-constructor,move-semantics,move-constructor,C++,C++11,Copy Constructor,Move Semantics,Move Constructor,我是C++11新手,发现移动语义和复制省略非常适合编写优雅高效的代码。但是我有一些问题想问一下。这里我编写了一个模板类matrix.hpp,并用它来测试移动语义的行为 #include <vector> #include <iostream> using namespace std; template<class T> class matrix { public: matrix(); // default constructor matrix

我是C++11新手,发现移动语义和复制省略非常适合编写优雅高效的代码。但是我有一些问题想问一下。这里我编写了一个模板类
matrix.hpp
,并用它来测试移动语义的行为

#include <vector>
#include <iostream>
using namespace std;

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) const;
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-assign" << 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) const {
    if (axis == 1) {
      matrix<T> mx(1, cols);
      // HERE compute mean vector ...
      return mx;
    } else if (axis == 0) {
      matrix<T> mx(rows, 1);
      // HERE compute mean vector ...
      return mx;
    }
}
结果是:

move-assign
--
copy-tor
move-assign
--
move-tor
move-assign
第一个结果是直接从移动分配的定义推断出来的。我猜第二个结果是编译器将创建对象*b的临时对象,然后std::move()将这个临时对象移动到a。第三个结果有点奇怪。但是,如果我在函数的作用域
mean(int轴)
中初始化本地对象
mx
,则只有一个
move assign
。谁能给我解释一下吗?谢谢


编辑我刚刚编辑了
平均值(int轴)
,就像在我的代码中一样。

没有任何优化,如下所示:

T fun() { T b; return b; }

int main() {
  T a;
  a = fun();
}
有两个步骤可以将返回的
b
转换为
a
。第一个是赋值表达式右侧的构造

a = /* object move constructed with value returned by fun() */
然后实际的赋值就发生了。由于
=
的右侧是右值,因此赋值通过移动来工作

对于第一个示例,返回的构造被省略,您只看到赋值的输出

对于第二种情况,由于您不返回本地,而是返回指向动态分配内存的指针,因此必须创建一个副本来执行返回。生成的副本将是一个右值,然后在分配中将其从移动到
a


对于第三个示例,由于
mx
是一个本地值,因此它被视为一个右值。返回的构造没有被省略(出于某种原因),但由于它是本地的,所以在返回的构造中它将被移动。因为返回的是一个右值,所以接下来会有一个移动任务。

@M.M谢谢!我已经更新了这个问题。@M.M.很抱歉。我重新键入我的实际代码,并试图简化它。
矩阵a(1,2)不编译,因为没有接受两个int的构造函数。代码中还存在其他几个编译错误。重要的是,您发布的程序可以完全按照编写的程序进行编译,并生成所需的输出。有关此过程的帮助,请参阅。@M.M我希望现在可以编译它。@M.M编写的最后一个案例已经符合标准下的副本省略条件。问题在于编译器的NRVO机制是否足够复杂,能够真正删除副本。(叮当声确实是这样。)谢谢你的澄清。然而,对于第三个示例,我只是更新了代码。我意识到
mean(int轴){matrix mx(row,col);/*..*/return mx}
只返回一个
move assign
。如果
mx
的局部声明是嵌套的,即
mean(int轴){If(axis==0){matrix mx(row,col);/*..*.*/return mx}或者If(axis==1){matrix mx(col,row);/*.*/return mx;}
,则出现原始结果。似乎作用域会产生一个额外的移动构造函数。如果你让情况变得更复杂,那么就无法执行。谢谢!复制省略真的很有用。它有点不赞成使用动态分配指针,至少在我的例子中是这样。
a = /* object move constructed with value returned by fun() */