题目

编写程序,剖析现代 C++ 的值类别系统(Value Categories):左值(lvalue)、纯右值(prvalue)与将亡值(xvalue)。通过编写重载函数精准捕获左值引用(T&)与右值引用(T&&);验证左值有确定物理内存地址可取地址(&val),而纯右值临时变量无法取地址;使用 std::move 进行无条件的静态类型强转,将左值强制转变为即将消亡的“将亡值”(xvalue)。

输入格式:无需外部输入。

输出格式:输出不同表达式传入函数时所命中的左值与右值重载版本判定。

数据范围与约定:C++11 核心值体系、取地址能力、移动语义语义学基础。

思路与算法

1. 值类别的金字塔
泛左值(glvalue)分为左值(lvalue)与将亡值(xvalue);右值(rvalue)分为将亡值(xvalue)与纯右值(prvalue)。

2. 取地址法则
能放在赋值号左边、有明确变量名、可以对其使用 & 取地址的表达式是左值;字面常量(如 42)、临时对象、表达式算术结果(如 a + b)是纯右值;通过 std::move(x) 强制转换得到的是将亡值。

3. 右值引用变量自身是左值
极其关键的认知点:在 void foo(int&& x) 中,参数 x 的类型是右值引用,但 x 作为一个有名变量,在其自身的作用域内它是一个有名字的“左值”!若继续往下传必须再次 std::move(x)。

完整程序

#include <iostream>
#include <string>

// 区分左值引用的重载
void classify(const std::string& str) {
    std::cout << "[左值引用重载] 接收到一个持久左值 (lvalue): \"" << str << "\"\n";
}

// 区分右值引用的重载
void classify(std::string&& str) {
    std::cout << "[右值引用重载] 接收到一个即将消亡的右值 (rvalue/xvalue): \"" << str << "\"\n";
}

std::string getTemporaryString() {
    return "Hello Temporary String";
}

int main() {
    std::string name = "Alice";

    std::cout << "--- 1. 传入具名变量 (左值) ---\n";
    classify(name); // 命中左值版本

    std::cout << "\n--- 2. 传入函数返回的纯右值 (prvalue) ---\n";
    classify(getTemporaryString()); // 命中右值版本

    std::cout << "\n--- 3. 传入字符串字面量构建的临时对象 ---\n";
    classify(std::string("Constructed On The Fly")); // 命中右值版本

    std::cout << "\n--- 4. 使用 std::move 将左值强制转变为将亡值 (xvalue) ---\n";
    // std::move 本身不移动任何数据,只是强制将左值类型转换为右值引用类型
    classify(std::move(name)); // 命中右值版本

    return 0;
}

运行示例

输出:

--- 1. 传入具名变量 (左值) ---
[左值引用重载] 接收到一个持久左值 (lvalue): "Alice"

--- 2. 传入函数返回的纯右值 (prvalue) ---
[右值引用重载] 接收到一个即将消亡的右值 (rvalue/xvalue): "Hello Temporary String"

--- 3. 传入字符串字面量构建的临时对象 ---
[右值引用重载] 接收到一个即将消亡的右值 (rvalue/xvalue): "Constructed On The Fly"

--- 4. 使用 std::move 将左值强制转变为将亡值 (xvalue) ---
[右值引用重载] 接收到一个即将消亡的右值 (rvalue/xvalue): "Alice"