C++学习笔记-Lvalues and Rvalues

左值(Lvalue)和右值(Rvalue)是C++中的基本概念,理解它们对于掌握现代C++的移动语义、完美转发等高级特性至关重要。左值是有持久存储支持的表达式,右值是临时值。

基本概念

 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
#include <iostream>

int GetValue()
{
    return 10;
}

/* 非常量左值引用只能接受左值 */
void SetValue1(int& value)
{
    // Do something
}

/* 常量左值引用同时接受左值和右值 */
void SetValue2(const int& value)
{
    // Do something
}

/* 非常量右值引用只能接受右值 */
void SetValue3(int&& value)
{
    // Do something
}

void BasicLvalueRvalueDemo()
{
    std::cout << "=== Basic Lvalue Rvalue Demo ===" << std::endl;
    
    int i = 10;  // i是左值,数字10是右值
    // 10 = i; // 右值不能被左值赋值
    
    int x = GetValue(); // 左值也可以是函数的结果
    
    std::cout << "Variable i: " << i << std::endl;
    std::cout << "Variable x: " << x << std::endl;
    
    /* 非常量左值引用只能接受左值 */
    SetValue1(i);
    // SetValue1(10); // 编译错误:不能将右值绑定到非常量左值引用
    
    /* 常量左值引用同时接受左值和右值 */
    SetValue2(i);
    SetValue2(10); // 可以,const左值引用也可以支持右值,实际上编译器为你创建了一个临时变量然后赋值给了那个引用。
    
    /* 非常量右值引用只能接受右值 */
    // SetValue3(i); // 编译错误:不能将左值绑定到右值引用
    SetValue3(10);
    SetValue3(GetValue());
    
    /*
    总结
    1. 左值是有某个持久存储支持的表达式,右值是临时值
    2. 非常量左值引用只能接受左值,常量左值引用同时接受左值和右值
    */
}

int main()
{
    BasicLvalueRvalueDemo();
    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
#include <iostream>
#include <string>
#include <vector>

class MyClass
{
public:
    int value;
    MyClass(int v) : value(v) {}
    
    // 左值版本的成员函数
    int& getValue() &
    {
        std::cout << "Lvalue version of getValue()" << std::endl;
        return value;
    }
    
    // 右值版本的成员函数
    int getValue() &&
    {
        std::cout << "Rvalue version of getValue()" << std::endl;
        return value;
    }
};

MyClass createObject(int v)
{
    return MyClass(v);
}

void ValueCategoryDemo()
{
    std::cout << "=== Value Category Demo ===" << std::endl;
    
    // 左值示例
    std::cout << "\n1. Lvalue examples:" << std::endl;
    int a = 5;              // a是左值
    int* ptr = &a;          // ptr是左值,&a取左值的地址
    int& ref = a;           // ref是左值引用
    std::string str = "hello"; // str是左值
    
    std::cout << "Variable a: " << a << std::endl;
    std::cout << "Pointer ptr: " << ptr << std::endl;
    std::cout << "Reference ref: " << ref << std::endl;
    std::cout << "String str: " << str << std::endl;
    
    // 右值示例
    std::cout << "\n2. Rvalue examples:" << std::endl;
    // 字面量是右值
    // 42, 3.14, "hello", true 都是右值
    
    // 函数返回值(按值返回)是右值
    int result = GetValue(); // GetValue()返回右值
    std::cout << "Function result: " << result << std::endl;
    
    // 临时对象是右值
    MyClass tempObj = createObject(100);
    std::cout << "Temporary object value: " << tempObj.value << std::endl;
    
    // 算术表达式结果是右值
    int sum = a + 5; // a + 5是右值
    std::cout << "Sum result: " << sum << std::endl;
    
    // 类型转换结果是右值
    double d = static_cast<double>(a); // static_cast结果是右值
    std::cout << "Cast result: " << d << std::endl;
}

void ReferenceCategoryDemo()
{
    std::cout << "\n=== Reference Category Demo ===" << std::endl;
    
    int x = 10;
    
    // 左值引用
    int& lref = x;          // 绑定到左值
    const int& clref = x;   // 常量左值引用绑定到左值
    const int& clref2 = 20; // 常量左值引用也可以绑定到右值
    
    std::cout << "Lvalue reference: " << lref << std::endl;
    std::cout << "Const lvalue reference 1: " << clref << std::endl;
    std::cout << "Const lvalue reference 2: " << clref2 << std::endl;
    
    // 右值引用
    int&& rref = 30;        // 绑定到右值
    int&& rref2 = GetValue(); // 绑定到函数返回的右值
    
    std::cout << "Rvalue reference 1: " << rref << std::endl;
    std::cout << "Rvalue reference 2: " << rref2 << std::endl;
    
    // 注意:右值引用变量本身是左值
    // int&& rref3 = rref; // 错误:rref是左值,不能绑定到右值引用
    int&& rref3 = std::move(rref); // 使用std::move将左值转换为右值
    std::cout << "Moved rvalue reference: " << rref3 << std::endl;
}

void MemberFunctionOverloadDemo()
{
    std::cout << "\n=== Member Function Overload Demo ===" << std::endl;
    
    MyClass obj(42);
    
    // 调用左值版本
    std::cout << "Calling on lvalue object:" << std::endl;
    int& val1 = obj.getValue(); // 调用左值版本
    std::cout << "Value: " << val1 << std::endl;
    
    // 调用右值版本
    std::cout << "\nCalling on rvalue object:" << std::endl;
    int val2 = createObject(84).getValue(); // 调用右值版本
    std::cout << "Value: " << val2 << std::endl;
    
    // 使用std::move强制调用右值版本
    std::cout << "\nUsing std::move:" << std::endl;
    int val3 = std::move(obj).getValue(); // 调用右值版本
    std::cout << "Value: " << val3 << std::endl;
}

int main()
{
    ValueCategoryDemo();
    ReferenceCategoryDemo();
    MemberFunctionOverloadDemo();
    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
#include <iostream>
#include <string>
#include <vector>
#include <utility>

// 演示左值和右值在函数重载中的应用
class StringProcessor
{
public:
    // 处理左值字符串(可能需要拷贝)
    void process(const std::string& str)
    {
        std::cout << "Processing lvalue string: " << str << std::endl;
        // 这里可能需要拷贝字符串
        data = str; // 拷贝
    }
    
    // 处理右值字符串(可以移动)
    void process(std::string&& str)
    {
        std::cout << "Processing rvalue string: " << str << std::endl;
        // 这里可以移动字符串,避免拷贝
        data = std::move(str); // 移动
    }
    
    void printData() const
    {
        std::cout << "Stored data: " << data << std::endl;
    }
    
private:
    std::string data;
};

// 完美转发示例
template<typename T>
void forwardToProcessor(StringProcessor& processor, T&& str)
{
    std::cout << "Forwarding to processor..." << std::endl;
    processor.process(std::forward<T>(str));
}

// 工厂函数示例
template<typename T, typename... Args>
T createObject(Args&&... args)
{
    std::cout << "Creating object with perfect forwarding..." << std::endl;
    return T(std::forward<Args>(args)...);
}

class Person
{
public:
    std::string name;
    int age;
    
    Person(const std::string& n, int a) : name(n), age(a)
    {
        std::cout << "Person constructor (lvalue string): " << name << std::endl;
    }
    
    Person(std::string&& n, int a) : name(std::move(n)), age(a)
    {
        std::cout << "Person constructor (rvalue string): " << name << std::endl;
    }
};

void PracticalApplicationsDemo()
{
    std::cout << "=== Practical Applications Demo ===" << std::endl;
    
    // 1. 函数重载优化
    std::cout << "\n1. Function Overload Optimization:" << std::endl;
    StringProcessor processor;
    
    std::string lvalueStr = "Hello World";
    processor.process(lvalueStr);           // 调用左值版本
    processor.process("Temporary String");   // 调用右值版本
    processor.process(std::string("Another Temp")); // 调用右值版本
    
    processor.printData();
    
    // 2. 完美转发
    std::cout << "\n2. Perfect Forwarding:" << std::endl;
    StringProcessor processor2;
    
    std::string anotherStr = "Forwarded String";
    forwardToProcessor(processor2, anotherStr);        // 转发左值
    forwardToProcessor(processor2, "Forwarded Temp");  // 转发右值
    
    processor2.printData();
    
    // 3. 工厂函数
    std::cout << "\n3. Factory Function:" << std::endl;
    
    std::string personName = "Alice";
    auto person1 = createObject<Person>(personName, 25);    // 左值
    auto person2 = createObject<Person>("Bob", 30);         // 右值
    auto person3 = createObject<Person>(std::string("Charlie"), 35); // 右值
    
    std::cout << "Person 1: " << person1.name << ", " << person1.age << std::endl;
    std::cout << "Person 2: " << person2.name << ", " << person2.age << std::endl;
    std::cout << "Person 3: " << person3.name << ", " << person3.age << std::endl;
}

// 容器操作示例
void ContainerOperationsDemo()
{
    std::cout << "\n=== Container Operations Demo ===" << std::endl;
    
    std::vector<std::string> strings;
    
    // 使用左值
    std::string str1 = "First String";
    strings.push_back(str1);           // 拷贝
    std::cout << "After push_back lvalue, str1: " << str1 << std::endl;
    
    // 使用右值
    strings.push_back("Second String"); // 移动(如果可能)
    
    // 显式移动
    std::string str2 = "Third String";
    strings.push_back(std::move(str2)); // 移动
    std::cout << "After move, str2: '" << str2 << "' (moved-from state)" << std::endl;
    
    // 使用emplace_back(直接构造)
    strings.emplace_back("Fourth String"); // 直接构造
    
    std::cout << "Vector contents:" << std::endl;
    for (size_t i = 0; i < strings.size(); ++i)
    {
        std::cout << "  [" << i << "]: " << strings[i] << std::endl;
    }
}

int main()
{
    PracticalApplicationsDemo();
    ContainerOperationsDemo();
    return 0;
}

移动语义和std::move

  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
#include <iostream>
#include <string>
#include <vector>
#include <utility>

// 演示移动语义的类
class MoveableClass
{
private:
    std::string* data;
    size_t size;
    
public:
    // 构造函数
    MoveableClass(const std::string& str) : size(str.length())
    {
        data = new std::string(str);
        std::cout << "Constructor: created with '" << *data << "'" << std::endl;
    }
    
    // 拷贝构造函数
    MoveableClass(const MoveableClass& other) : size(other.size)
    {
        data = new std::string(*other.data);
        std::cout << "Copy constructor: copied '" << *data << "'" << std::endl;
    }
    
    // 移动构造函数
    MoveableClass(MoveableClass&& other) noexcept : data(other.data), size(other.size)
    {
        other.data = nullptr;
        other.size = 0;
        std::cout << "Move constructor: moved '" << *data << "'" << std::endl;
    }
    
    // 拷贝赋值操作符
    MoveableClass& operator=(const MoveableClass& other)
    {
        if (this != &other)
        {
            delete data;
            data = new std::string(*other.data);
            size = other.size;
            std::cout << "Copy assignment: copied '" << *data << "'" << std::endl;
        }
        return *this;
    }
    
    // 移动赋值操作符
    MoveableClass& operator=(MoveableClass&& other) noexcept
    {
        if (this != &other)
        {
            delete data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
            std::cout << "Move assignment: moved '" << *data << "'" << std::endl;
        }
        return *this;
    }
    
    // 析构函数
    ~MoveableClass()
    {
        if (data)
        {
            std::cout << "Destructor: destroying '" << *data << "'" << std::endl;
            delete data;
        }
        else
        {
            std::cout << "Destructor: destroying moved-from object" << std::endl;
        }
    }
    
    // 获取数据
    const std::string& getData() const
    {
        return data ? *data : static_cast<const std::string&>(std::string(""));
    }
    
    bool isValid() const { return data != nullptr; }
};

// 工厂函数
MoveableClass createMoveableObject(const std::string& str)
{
    return MoveableClass(str);
}

void MoveSemanticsDemo()
{
    std::cout << "=== Move Semantics Demo ===" << std::endl;
    
    // 1. 基本移动操作
    std::cout << "\n1. Basic move operations:" << std::endl;
    
    MoveableClass obj1("Original Object");
    std::cout << "obj1 data: " << obj1.getData() << std::endl;
    
    // 拷贝构造
    MoveableClass obj2 = obj1;
    std::cout << "After copy, obj1 data: " << obj1.getData() << std::endl;
    std::cout << "After copy, obj2 data: " << obj2.getData() << std::endl;
    
    // 移动构造
    MoveableClass obj3 = std::move(obj1);
    std::cout << "After move, obj1 valid: " << obj1.isValid() << std::endl;
    std::cout << "After move, obj3 data: " << obj3.getData() << std::endl;
    
    // 2. 函数返回值优化
    std::cout << "\n2. Return value optimization:" << std::endl;
    
    MoveableClass obj4 = createMoveableObject("Factory Created");
    std::cout << "obj4 data: " << obj4.getData() << std::endl;
    
    // 3. 容器中的移动
    std::cout << "\n3. Move in containers:" << std::endl;
    
    std::vector<MoveableClass> vec;
    
    MoveableClass temp("Temp Object");
    vec.push_back(std::move(temp)); // 移动到容器中
    std::cout << "After move to vector, temp valid: " << temp.isValid() << std::endl;
    
    vec.emplace_back("Emplaced Object"); // 直接构造
    
    std::cout << "Vector contents:" << std::endl;
    for (size_t i = 0; i < vec.size(); ++i)
    {
        std::cout << "  [" << i << "]: " << vec[i].getData() << std::endl;
    }
}

// std::move的实现原理演示
template<typename T>
constexpr typename std::remove_reference<T>::type&& my_move(T&& t) noexcept
{
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

void MoveImplementationDemo()
{
    std::cout << "\n=== Move Implementation Demo ===" << std::endl;
    
    std::string str = "Test String";
    std::cout << "Original string: " << str << std::endl;
    
    // 使用标准std::move
    std::string moved1 = std::move(str);
    std::cout << "After std::move, original: '" << str << "'" << std::endl;
    std::cout << "Moved string: " << moved1 << std::endl;
    
    // 重置字符串
    str = "Another Test";
    
    // 使用自定义my_move
    std::string moved2 = my_move(str);
    std::cout << "After my_move, original: '" << str << "'" << std::endl;
    std::cout << "Moved string: " << moved2 << std::endl;
    
    std::cout << "\nNote: std::move doesn't actually move anything!" << std::endl;
    std::cout << "It just casts lvalue to rvalue reference." << std::endl;
    std::cout << "The actual move happens in move constructor/assignment." << std::endl;
}

// 条件移动示例
template<typename T>
void conditionalMove(std::vector<T>& dest, std::vector<T>& source, bool shouldMove)
{
    if (shouldMove)
    {
        std::cout << "Moving elements..." << std::endl;
        for (auto& item : source)
        {
            dest.push_back(std::move(item));
        }
    }
    else
    {
        std::cout << "Copying elements..." << std::endl;
        for (const auto& item : source)
        {
            dest.push_back(item);
        }
    }
}

void ConditionalMoveDemo()
{
    std::cout << "\n=== Conditional Move Demo ===" << std::endl;
    
    std::vector<MoveableClass> source;
    source.emplace_back("Item 1");
    source.emplace_back("Item 2");
    
    std::vector<MoveableClass> dest1, dest2;
    
    std::cout << "\nCopying:" << std::endl;
    conditionalMove(dest1, source, false);
    
    std::cout << "\nMoving:" << std::endl;
    conditionalMove(dest2, source, true);
    
    std::cout << "\nSource after operations:" << std::endl;
    for (size_t i = 0; i < source.size(); ++i)
    {
        std::cout << "  [" << i << "] valid: " << source[i].isValid() << std::endl;
    }
}

int main()
{
    MoveSemanticsDemo();
    MoveImplementationDemo();
    ConditionalMoveDemo();
    return 0;
}

总结

  1. 基本概念
    • 左值:有持久存储支持的表达式(变量、引用等)
    • 右值:临时值(字面量、函数返回值、临时对象等)
  2. 引用类型
    • 左值引用(T&):只能绑定左值
    • 常量左值引用(const T&):可以绑定左值和右值
    • 右值引用(T&&):只能绑定右值
  3. 实际应用
    • 函数重载优化(避免不必要的拷贝)
    • 完美转发(保持参数的值类别)
    • 移动语义(高效的资源转移)
    • 容器操作优化
  4. 移动语义
    • std::move将左值转换为右值引用
    • 移动构造函数和移动赋值操作符
    • 避免深拷贝,提高性能
  5. 最佳实践
    • 理解何时使用移动vs拷贝
    • 正确实现移动构造函数和移动赋值
    • 使用std::forward进行完美转发
    • 在容器操作中合理使用移动
  6. 注意事项
    • std::move本身不移动任何东西,只是类型转换
    • 移动后的对象处于有效但未指定状态
    • 右值引用变量本身是左值
updatedupdated2025-09-202025-09-20