C++ 移动赋值和移动构造函数都是从函数调用发出的
我是C++11新手,发现移动语义和复制省略非常适合编写优雅高效的代码。但是我有一些问题想问一下。这里我编写了一个模板类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
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() */