这两天在自己复现那本中文翻译出了名的烂的C++高并发编程实践里的线程池那一章节的代码,偶然发现了一个关于移动构造函数的问题,还别说,要是不注意,还是挺容易犯这个错误的
举个例子:
#include <string>
#include <iostream>
#include <iomanip>
#include <utility>
class A {
private:
int a;
public:
A() {}
A(int a_value){a = a_value;}
int get_value() {return a;}
~A(){}
A(A&& a_) {a = std::move(a_.a);}
A& operator = (A &&a_)
{
a = std::move(a_.a);
return *this;
}
};
void f(A &a1, A &a2)
{
a2 = std::move(a1);
}
int main()
{
A a1(2);
std::cout << a1.get_value() << std::endl;
A a2;
f(a1, a2);
std::cout << a2.get_value() << std::endl;
return 0;
}
这时如果你编译该代码,会发现以下的问题:
main.cpp:34:22: error: use of deleted function ‘constexpr A& A::operator=(const A&)’
a1 = std::move(a2);
如果你写成这样则没有问题:
#include <string>
#include <iostream>
#include <iomanip>
#include <utility>
class A {
private:
int a;
public:
A() {}
A(int a_value){a = a_value;}
int get_value() {return a;}
~A(){}
A(A&& a_) {a = std::move(a_.a);}
};
int main()
{
A a1(2);
std::cout << a1.get_value() << std::endl;
A a2 = std::move(a1);
std::cout << a2.get_value() << std::endl;
return 0;
}
看出问题在哪里了吗,问题就在第一种情况使用了a2的传参引用,也就是说这时候a2已经在内存里完成了构造,而第二种情况的a2则是直接利用A的移动构造函数来实现构造
这也就解释了第一种情况无法通过编译的原因,因为实际上第一种情况里,因为自定义了移动构造函数,则默认的移动构造函数和移动等号运算符被删除,用户必须自定义一个移动等号运算符重载才能通过编译,如下:
#include <string>
#include <iostream>
#include <iomanip>
#include <utility>
class A {
private:
int a;
public:
A() {}
A(int a_value){a = a_value;}
int get_value() {return a;}
~A(){}
A(A&& a_) {a = std::move(a_.a);}
A& operator = (A &&a_)
{
a = std::move(a_.a);
return *this;
}
};
void f(A &a1, A &a2)
{
a2 = std::move(a1);
}
int main()
{
A a1(2);
std::cout << a1.get_value() << std::endl;
A a2;
f(a1, a2);
std::cout << a2.get_value() << std::endl;
return 0;
}
当然这种情况下你可以只定义移动赋值运算符,不过我个人还是建议如果不使用默认的移动构造函数,则最好同时定义好移动构造函数和移动复制运算符
网友评论