C++学习笔记-Move Semantics

移动语义(Move Semantics)是C++11引入的重要特性,通过"窃取"资源而不是拷贝资源来提高程序性能。移动语义允许我们将资源从一个对象转移到另一个对象,避免昂贵的深拷贝操作。

移动构造函数基础

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>

class String
{
public:
    String(const char* str)
    {
        std::cout << "Created...\n";
        m_Size = strlen(str);
        m_Buffer = new char[m_Size + 1];
        memcpy(m_Buffer, str, m_Size);
        m_Buffer[m_Size] = 0;
    }

    String(const String& other)
    {
        std::cout << "Copied...\n";
        m_Size = other.m_Size;
        m_Buffer = new char[m_Size + 1];
        memcpy(m_Buffer, other.m_Buffer, m_Size);
        m_Buffer[m_Size] = 0;
    }

    /* 移动构造函数,这里是浅拷贝,"偷取"数据 */
    String(String&& other) noexcept
    {
        std::cout << "Move...\n";
        m_Size = other.m_Size;
        m_Buffer = other.m_Buffer;

        // 为了防止析构函数释放时销毁数据,需要将旧指针置空
        other.m_Size = 0;
        other.m_Buffer = nullptr;
    }

    ~String()
    {
        delete[] m_Buffer;
    }

    void Print() { std::cout << m_Buffer << "\n"; }

private:
    char* m_Buffer;
    size_t m_Size;
};

class Entity
{
public:
    Entity(const String& name) : m_Name(name) {}

    /* 移动构造函数,为了确保调用移动构造函数,需要用std::move()强制转换成(String&&) */
    //Entity(String&& name) : m_Name((String&&)name) {} // 这样也是可以的
    Entity(String&& name) : m_Name(std::move(name)) {}

    void PrintName()
    {
        m_Name.Print();
    }
private:
    String m_Name;
};

void BasicMoveSemanticsDemo()
{
    std::cout << "=== Basic Move Semantics Demo ===" << std::endl;
    
    Entity entity(String("Jerry"));
    entity.PrintName();

    /*
    总结
    1. 移动构造函数接受一个右值引用
    2. 移动构造函数是浅拷贝,为了防止析构函数释放时销毁数据,需要将旧指针置空
    */
}

int main()
{
    BasicMoveSemanticsDemo();
    return 0;
}

移动语义的详细实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Resource
{
private:
    int* data;
    size_t size;
    std::string name;
    
public:
    // 构造函数
    Resource(const std::string& n, size_t s) : name(n), size(s)
    {
        data = new int[size];
        for (size_t i = 0; i < size; ++i)
        {
            data[i] = static_cast<int>(i);
        }
        std::cout << "Resource '" << name << "' created with " << size << " elements" << std::endl;
    }
    
    // 拷贝构造函数
    Resource(const Resource& other) : name(other.name + "_copy"), size(other.size)
    {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        std::cout << "Resource '" << name << "' copied from '" << other.name << "'" << std::endl;
    }
    
    // 移动构造函数
    Resource(Resource&& other) noexcept 
        : data(other.data), size(other.size), name(std::move(other.name))
    {
        // 将源对象置于有效但未指定的状态
        other.data = nullptr;
        other.size = 0;
        // other.name 已经被移动,处于有效但未指定状态
        
        std::cout << "Resource '" << name << "' moved" << std::endl;
    }
    
    // 拷贝赋值操作符
    Resource& operator=(const Resource& other)
    {
        if (this != &other)
        {
            // 清理当前资源
            delete[] data;
            
            // 拷贝新资源
            name = other.name + "_assigned";
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);
            
            std::cout << "Resource '" << name << "' copy assigned from '" << other.name << "'" << std::endl;
        }
        return *this;
    }
    
    // 移动赋值操作符
    Resource& operator=(Resource&& other) noexcept
    {
        if (this != &other)
        {
            // 清理当前资源
            delete[] data;
            
            // 移动新资源
            data = other.data;
            size = other.size;
            name = std::move(other.name);
            
            // 将源对象置于有效但未指定的状态
            other.data = nullptr;
            other.size = 0;
            
            std::cout << "Resource '" << name << "' move assigned" << std::endl;
        }
        return *this;
    }
    
    // 析构函数
    ~Resource()
    {
        if (data)
        {
            std::cout << "Resource '" << name << "' destroyed" << std::endl;
            delete[] data;
        }
        else
        {
            std::cout << "Empty resource destroyed" << std::endl;
        }
    }
    
    // 工具函数
    void print() const
    {
        std::cout << "Resource '" << name << "' has " << size << " elements";
        if (data && size > 0)
        {
            std::cout << " [" << data[0];
            if (size > 1) std::cout << ", " << data[1];
            if (size > 2) std::cout << ", ...";
            std::cout << "]";
        }
        std::cout << std::endl;
    }
    
    bool isValid() const { return data != nullptr; }
    const std::string& getName() const { return name; }
    size_t getSize() const { return size; }
};

void DetailedMoveSemanticsDemo()
{
    std::cout << "=== Detailed Move Semantics Demo ===" << std::endl;
    
    // 1. 基本移动操作
    std::cout << "\n1. Basic move operations:" << std::endl;
    
    Resource res1("original", 5);
    res1.print();
    
    // 拷贝构造
    Resource res2 = res1;
    res1.print();
    res2.print();
    
    // 移动构造
    Resource res3 = std::move(res1);
    std::cout << "After move construction:" << std::endl;
    std::cout << "  res1 valid: " << res1.isValid() << std::endl;
    res3.print();
    
    // 2. 赋值操作
    std::cout << "\n2. Assignment operations:" << std::endl;
    
    Resource res4("target", 3);
    Resource res5("source", 7);
    
    res4.print();
    res5.print();
    
    // 拷贝赋值
    res4 = res5;
    std::cout << "After copy assignment:" << std::endl;
    res4.print();
    res5.print();
    
    // 移动赋值
    Resource res6("another_target", 2);
    res6 = std::move(res5);
    std::cout << "After move assignment:" << std::endl;
    res6.print();
    std::cout << "  res5 valid: " << res5.isValid() << std::endl;
}

// 工厂函数演示
Resource createResource(const std::string& name, size_t size)
{
    return Resource(name, size);
}

void FactoryFunctionDemo()
{
    std::cout << "\n=== Factory Function Demo ===" << std::endl;
    
    // 函数返回值会自动使用移动语义(如果可能)
    Resource res = createResource("factory_created", 10);
    res.print();
    
    // 显式移动
    std::vector<Resource> resources;
    
    Resource temp("temp_resource", 5);
    resources.push_back(std::move(temp)); // 移动到容器中
    
    std::cout << "After moving to container:" << std::endl;
    std::cout << "  temp valid: " << temp.isValid() << std::endl;
    resources[0].print();
    
    // 直接构造到容器中
    resources.emplace_back("emplaced_resource", 8);
    
    std::cout << "Container contents:" << std::endl;
    for (size_t i = 0; i < resources.size(); ++i)
    {
        std::cout << "  [" << i << "]: ";
        resources[i].print();
    }
}

int main()
{
    DetailedMoveSemanticsDemo();
    FactoryFunctionDemo();
    return 0;
}

移动语义的性能优化

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <algorithm>

// 性能测试类
class PerformanceTestObject
{
private:
    std::vector<int> data;
    std::string name;
    
public:
    PerformanceTestObject(const std::string& n, size_t size) : name(n)
    {
        data.resize(size);
        std::iota(data.begin(), data.end(), 0);
    }
    
    // 拷贝构造函数
    PerformanceTestObject(const PerformanceTestObject& other)
        : data(other.data), name(other.name + "_copy")
    {
        // 模拟昂贵的拷贝操作
    }
    
    // 移动构造函数
    PerformanceTestObject(PerformanceTestObject&& other) noexcept
        : data(std::move(other.data)), name(std::move(other.name))
    {
        // 移动操作,非常快
    }
    
    // 拷贝赋值
    PerformanceTestObject& operator=(const PerformanceTestObject& other)
    {
        if (this != &other)
        {
            data = other.data;
            name = other.name + "_assigned";
        }
        return *this;
    }
    
    // 移动赋值
    PerformanceTestObject& operator=(PerformanceTestObject&& other) noexcept
    {
        if (this != &other)
        {
            data = std::move(other.data);
            name = std::move(other.name);
        }
        return *this;
    }
    
    size_t size() const { return data.size(); }
    const std::string& getName() const { return name; }
};

void PerformanceComparisonDemo()
{
    std::cout << "=== Performance Comparison Demo ===" << std::endl;
    
    const size_t objectSize = 100000;
    const size_t iterations = 1000;
    
    // 测试拷贝性能
    auto start = std::chrono::high_resolution_clock::now();
    
    std::vector<PerformanceTestObject> copyVector;
    for (size_t i = 0; i < iterations; ++i)
    {
        PerformanceTestObject obj("test_" + std::to_string(i), objectSize);
        copyVector.push_back(obj); // 拷贝
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto copyTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    // 测试移动性能
    start = std::chrono::high_resolution_clock::now();
    
    std::vector<PerformanceTestObject> moveVector;
    for (size_t i = 0; i < iterations; ++i)
    {
        PerformanceTestObject obj("test_" + std::to_string(i), objectSize);
        moveVector.push_back(std::move(obj)); // 移动
    }
    
    end = std::chrono::high_resolution_clock::now();
    auto moveTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << "Copy operations: " << copyTime.count() << " ms" << std::endl;
    std::cout << "Move operations: " << moveTime.count() << " ms" << std::endl;
    std::cout << "Speedup: " << (double)copyTime.count() / moveTime.count() << "x" << std::endl;
    
    // 测试容器重新分配
    std::cout << "\nContainer reallocation test:" << std::endl;
    
    std::vector<PerformanceTestObject> testVector;
    
    start = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 100; ++i)
    {
        testVector.emplace_back("item_" + std::to_string(i), 1000);
    }
    end = std::chrono::high_resolution_clock::now();
    auto emplaceTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "Emplace operations: " << emplaceTime.count() << " microseconds" << std::endl;
    std::cout << "Vector size: " << testVector.size() << std::endl;
    std::cout << "Vector capacity: " << testVector.capacity() << std::endl;
}

// 移动语义在算法中的应用
void AlgorithmOptimizationDemo()
{
    std::cout << "\n=== Algorithm Optimization Demo ===" << std::endl;
    
    std::vector<PerformanceTestObject> objects;
    
    // 创建一些对象
    for (int i = 0; i < 10; ++i)
    {
        objects.emplace_back("object_" + std::to_string(i), 1000);
    }
    
    std::cout << "Original order:" << std::endl;
    for (size_t i = 0; i < std::min(size_t(5), objects.size()); ++i)
    {
        std::cout << "  " << objects[i].getName() << std::endl;
    }
    
    // 使用移动语义的排序(基于名称)
    auto start = std::chrono::high_resolution_clock::now();
    
    std::sort(objects.begin(), objects.end(), 
              [](const PerformanceTestObject& a, const PerformanceTestObject& b) {
                  return a.getName() < b.getName();
              });
    
    auto end = std::chrono::high_resolution_clock::now();
    auto sortTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "\nAfter sorting:" << std::endl;
    for (size_t i = 0; i < std::min(size_t(5), objects.size()); ++i)
    {
        std::cout << "  " << objects[i].getName() << std::endl;
    }
    
    std::cout << "Sort time: " << sortTime.count() << " microseconds" << std::endl;
    
    // 移动元素到另一个容器
    std::vector<PerformanceTestObject> movedObjects;
    movedObjects.reserve(objects.size());
    
    start = std::chrono::high_resolution_clock::now();
    for (auto& obj : objects)
    {
        movedObjects.push_back(std::move(obj));
    }
    end = std::chrono::high_resolution_clock::now();
    auto moveAllTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "Move all elements time: " << moveAllTime.count() << " microseconds" << std::endl;
    std::cout << "Moved objects count: " << movedObjects.size() << std::endl;
}

int main()
{
    PerformanceComparisonDemo();
    AlgorithmOptimizationDemo();
    return 0;
}

移动语义的最佳实践

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <utility>

// 正确实现移动语义的示例类
class BestPracticeClass
{
private:
    std::unique_ptr<int[]> data;
    size_t size;
    std::string name;

public:
    // 构造函数
    BestPracticeClass(const std::string& n, size_t s)
        : data(std::make_unique<int[]>(s)), size(s), name(n)
    {
        for (size_t i = 0; i < size; ++i)
        {
            data[i] = static_cast<int>(i);
        }
        std::cout << "Constructed: " << name << std::endl;
    }

    // 拷贝构造函数
    BestPracticeClass(const BestPracticeClass& other)
        : data(std::make_unique<int[]>(other.size)), size(other.size), name(other.name + "_copy")
    {
        std::copy(other.data.get(), other.data.get() + size, data.get());
        std::cout << "Copy constructed: " << name << std::endl;
    }

    // 移动构造函数 - 标记为noexcept
    BestPracticeClass(BestPracticeClass&& other) noexcept
        : data(std::move(other.data)), size(other.size), name(std::move(other.name))
    {
        other.size = 0; // 将源对象置于有效状态
        std::cout << "Move constructed: " << name << std::endl;
    }

    // 拷贝赋值操作符
    BestPracticeClass& operator=(const BestPracticeClass& other)
    {
        if (this != &other)
        {
            // 创建临时对象,然后交换(异常安全)
            BestPracticeClass temp(other);
            swap(temp);
            std::cout << "Copy assigned: " << name << std::endl;
        }
        return *this;
    }

    // 移动赋值操作符 - 标记为noexcept
    BestPracticeClass& operator=(BestPracticeClass&& other) noexcept
    {
        if (this != &other)
        {
            data = std::move(other.data);
            size = other.size;
            name = std::move(other.name);
            other.size = 0;
            std::cout << "Move assigned: " << name << std::endl;
        }
        return *this;
    }

    // 交换函数
    void swap(BestPracticeClass& other) noexcept
    {
        std::swap(data, other.data);
        std::swap(size, other.size);
        std::swap(name, other.name);
    }

    // 析构函数
    ~BestPracticeClass()
    {
        std::cout << "Destructed: " << name << std::endl;
    }

    // 访问器
    const std::string& getName() const { return name; }
    size_t getSize() const { return size; }
    bool isValid() const { return data != nullptr; }

    void print() const
    {
        std::cout << "Object: " << name << ", size: " << size;
        if (data && size > 0)
        {
            std::cout << ", first element: " << data[0];
        }
        std::cout << std::endl;
    }
};

// 全局swap函数(ADL查找)
void swap(BestPracticeClass& a, BestPracticeClass& b) noexcept
{
    a.swap(b);
}

void BestPracticesDemo()
{
    std::cout << "=== Best Practices Demo ===" << std::endl;

    // 1. 正确的移动语义实现
    std::cout << "\n1. Proper move semantics implementation:" << std::endl;

    BestPracticeClass obj1("original", 5);
    obj1.print();

    BestPracticeClass obj2 = std::move(obj1);
    obj2.print();
    std::cout << "obj1 after move - valid: " << obj1.isValid() << std::endl;

    // 2. 容器中的移动语义
    std::cout << "\n2. Move semantics in containers:" << std::endl;

    std::vector<BestPracticeClass> container;
    container.reserve(3); // 避免重新分配

    container.emplace_back("item1", 10);
    container.emplace_back("item2", 20);

    BestPracticeClass temp("temp", 15);
    container.push_back(std::move(temp));

    std::cout << "Container contents:" << std::endl;
    for (const auto& item : container)
    {
        item.print();
    }

    // 3. 函数参数和返回值
    std::cout << "\n3. Function parameters and return values:" << std::endl;

    auto factory = [](const std::string& name, size_t size) -> BestPracticeClass {
        return BestPracticeClass(name, size);
    };

    auto processObject = [](BestPracticeClass obj) { // 按值传递,支持移动
        obj.print();
        return obj; // 返回值优化
    };

    BestPracticeClass created = factory("factory_created", 8);
    BestPracticeClass processed = processObject(std::move(created));
    processed.print();
}

// 移动语义的常见陷阱
void CommonPitfallsDemo()
{
    std::cout << "\n=== Common Pitfalls Demo ===" << std::endl;

    // 陷阱1:多次移动同一个对象
    std::cout << "\n1. Multiple moves from same object:" << std::endl;
    BestPracticeClass original("original", 5);

    BestPracticeClass moved1 = std::move(original);
    // BestPracticeClass moved2 = std::move(original); // 危险:从已移动的对象再次移动

    std::cout << "After first move, original valid: " << original.isValid() << std::endl;

    // 陷阱2:忘记std::move
    std::cout << "\n2. Forgetting std::move:" << std::endl;
    BestPracticeClass source("source", 3);

    // BestPracticeClass dest = source; // 拷贝,不是移动
    BestPracticeClass dest = std::move(source); // 正确的移动

    // 陷阱3:移动const对象
    std::cout << "\n3. Moving const objects:" << std::endl;
    const BestPracticeClass constObj("const", 2);
    // BestPracticeClass moved = std::move(constObj); // 实际上是拷贝,不是移动

    // 陷阱4:返回局部变量时使用std::move
    std::cout << "\n4. Unnecessary std::move on return:" << std::endl;

    auto badFactory = []() -> BestPracticeClass {
        BestPracticeClass local("local", 4);
        return std::move(local); // 不必要,阻止了RVO
    };

    auto goodFactory = []() -> BestPracticeClass {
        BestPracticeClass local("local", 4);
        return local; // 编译器会自动优化
    };

    BestPracticeClass result = goodFactory();
    result.print();
}

// 移动语义的设计指导原则
void DesignGuidelinesDemo()
{
    std::cout << "\n=== Design Guidelines Demo ===" << std::endl;

    std::cout << "Move semantics design guidelines:" << std::endl;
    std::cout << "1. Always mark move operations as noexcept when possible" << std::endl;
    std::cout << "2. Leave moved-from objects in a valid but unspecified state" << std::endl;
    std::cout << "3. Implement move operations for resource-owning classes" << std::endl;
    std::cout << "4. Use std::move only when you want to transfer ownership" << std::endl;
    std::cout << "5. Don't return std::move(local_variable) from functions" << std::endl;
    std::cout << "6. Consider the Rule of Five (destructor, copy ctor, copy assign, move ctor, move assign)" << std::endl;
    std::cout << "7. Use smart pointers to automatically get move semantics" << std::endl;
    std::cout << "8. Be careful with self-assignment in move operations" << std::endl;

    // 演示智能指针的自动移动语义
    std::cout << "\nSmart pointer automatic move semantics:" << std::endl;

    auto ptr1 = std::make_unique<int>(42);
    std::cout << "ptr1 value: " << *ptr1 << std::endl;

    auto ptr2 = std::move(ptr1);
    std::cout << "After move, ptr1 is: " << (ptr1 ? "valid" : "null") << std::endl;
    std::cout << "ptr2 value: " << *ptr2 << std::endl;
}

int main()
{
    BestPracticesDemo();
    CommonPitfallsDemo();
    DesignGuidelinesDemo();
    return 0;
}

总结

  1. 移动语义基础:通过"窃取"资源而不是拷贝来提高性能
  2. 核心组件
    • 移动构造函数:T(T&& other) noexcept
    • 移动赋值操作符:T& operator=(T&& other) noexcept
    • std::move:将左值转换为右值引用
  3. 性能优势
    • 避免昂贵的深拷贝操作
    • 减少内存分配和释放
    • 提高容器操作效率
    • 优化函数参数传递和返回值
  4. 实现要点
    • 标记为noexcept(如果可能)
    • 将源对象置于有效但未指定状态
    • 处理自赋值情况
    • 遵循Rule of Five
  5. 最佳实践
    • 为资源拥有类实现移动语义
    • 使用智能指针自动获得移动语义
    • 在容器操作中合理使用移动
    • 避免多次移动同一对象
  6. 常见陷阱
    • 忘记使用std::move
    • 移动const对象(实际是拷贝)
    • 返回局部变量时不必要的std::move
    • 从已移动对象再次移动
updatedupdated2025-09-202025-09-20