为什么我在 class 中使用用户定义的析构函数获得隐式移动构造函数

Why do I get an implicit move constructor in a class with a user-defined destructor

我对隐式移动构造函数的某些方面感到困惑。

我的理解是编译器为 class 提供了隐式声明的移动构造函数,前提是没有用户声明的复制构造函数、复制赋值运算符、移动赋值运算符和析构函数。

在我的示例中 'Heavy' 就是这种情况。其行为符合预期(数据已移动)。

'HeavyWithDestructor' 不符合隐式声明的移动构造函数的条件,因为它有一个析构函数,但我可以“std::move”它。有点,它是一个副本(见数据指针)。

在我看来,这就像一个普通移动构造函数,因为它执行与普通复制构造函数相同的操作(好像 std::memmove).

但如果我一开始就没有创建隐式移动构造函数的条件,那怎么可能是一个微不足道的移动构造函数。此外,'std::is_trivially_move_constructible_v' 表示这不是一个简单的移动构造函数。

    #include <iostream>
    #include <vector>
    #include <type_traits>
    
    using namespace std;
    
    constexpr int largeNumber = 10000000;
    #define OUT(...) std::cout << #__VA_ARGS__ << " : " << __VA_ARGS__ << '\n'
    
    // Consistent with an implicit 'move' constructor.
    class Heavy
    {
        vector<int> v_;
    public:
        Heavy() : v_(vector<int>(largeNumber)) {}
    
        int* getDatap() { return v_.data(); }
    };
    
    // Not consistent with an implicit 'move' constructor. (Because has a destructor)
    class HeavyWithDestructor
    {
        vector<int> v_;
    public:
        HeavyWithDestructor() : v_(vector<int>(largeNumber)) {}
        ~HeavyWithDestructor(){}
    
        int* getDatap() { return v_.data(); }
    };
    
    int main()
    {
        cout << "Moving a heavy object" << endl;
    
        OUT(std::is_move_constructible_v<Heavy>);
        OUT(std::is_trivially_move_constructible_v<Heavy>);
    
        Heavy originalHeavy;
        cout << "Data* in original() -> " << originalHeavy.getDatap() << endl;
        Heavy finalHeavy = move(originalHeavy);
        cout << "Data* in main()     -> " << finalHeavy.getDatap() << endl << endl;
    
    
        cout << "Moving a heavy object with a destructor" << endl;
    
        OUT(std::is_move_constructible_v<HeavyWithDestructor>);
        OUT(std::is_trivially_move_constructible_v<HeavyWithDestructor>);
    
        HeavyWithDestructor originalWoDestructor;
        cout << "Data* in original() -> " << originalWoDestructor.getDatap() << endl;
        HeavyWithDestructor finalWoDestructor = move(originalWoDestructor);
        cout << "Data* in main()     -> " << finalWoDestructor.getDatap() << endl;
    
        return 0;
    }

我得到以下输出:我可以确认我正在移动 'Heavy' 导致指向矢量数据的指针指向同一位置。我还可以确认 'HeavyWithDestructor' 正在复制,而不是移动数据。

Moving a heavy object
std::is_move_constructible_v<Heavy> : 1
std::is_trivially_move_constructible_v<Heavy> : 0
Data* in original() -> 000001E3FB193080
Data* in main()     -> 000001E3FB193080

Moving a heavy object with a destructor
std::is_move_constructible_v<HeavyWithDestructor> : 1
std::is_trivially_move_constructible_v<HeavyWithDestructor> : 0
Data* in original() -> 000001E3FD7C6080
Data* in main()     -> 000001E38000A080

编译器为 'HeavyWithDestructor' 声明的构造函数是什么?。如果这个构造函数没有移动数据,为什么我仍然可以对它使用 std::move? 如果我更努力地让编译器不通过定义一个复制构造函数来为我声明一个移动构造函数,那么我就不能使用 std::move。我收到编译错误。这是我所期望的。由此,我收集到的构造函数不是复制构造函数。我最初怀疑这是一个微不足道的移动构造函数(其行为与 std::memmove 中一样),但我有迹象表明这也不正确。那这是什么?

我正在使用 vs2019 c++17 作为编译器。

HeavyWithDestructor是典型的C++03类型:可复制但不可移动(什么是“可移动”?)。因此,为了兼容性,每当尝试移动时都会复制它。这样做的技术原因是 const HeavyWithDestructor& 可以绑定到右值;道德原因是 std::move 一如既往地授予 移动某些东西的权限 但不需要它(或自己这样做)。

(您对复制构造函数的实验不够详细,无法重现,但可能涉及 HeavyWithDestructor(HeavyWithDestructor&),它仍被视为复制构造函数,但不能 serve 作为移动构造函数。)