C/C++

移动构造函数常见错误(一)

2021-12-19  本文已影响0人  门口的野蛮人

这两天在自己复现那本中文翻译出了名的烂的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;
}

当然这种情况下你可以只定义移动赋值运算符,不过我个人还是建议如果不使用默认的移动构造函数,则最好同时定义好移动构造函数和移动复制运算符

上一篇 下一篇

猜你喜欢

热点阅读