You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

即将离开作用域的C++变量属于lvalue还是xvalue?

当一个即将离开作用域的变量被返回或抛出时,其资源可以被复用,也就是说可以对其执行moved from(移动操作),如下方C++程序所示:

#include <iostream>

struct X {
    X() {
        std::cout << "X()\n";
    }
    
    X(X&&) {
        std::cout << "X(X&&)\n";
    }
    
    ~X() {
        std::cout << "~X()\n";
    }
};

X f() {
    X x;
    std::cout << "return\n";
    return x;
}

int main() {
    try {
        X x = f();
        std::cout << "throw\n";
        throw x;
    } catch (...) {
        std::cout << "catch\n";
    }
    return 0;
}

编译(我们使用-fno-elide-constructors编译选项关闭复制/移动消除)、链接与执行命令如下:

clang++ -std=c++17 -O2 -Wall -pedantic -pthread -fno-elide-constructors main.cpp && ./a.out

运行输出如下:

X()
return
X(X&&)
~X()
throw
X(X&&)
~X()
catch
~X()

上述代码中return x;与throw x;语句里的x指代的就是资源可被复用的对象。

在《C++编程语言标准工作草案》的[basic.lval-1]章节中,对值类别给出了如下定义:

  • glvalue是一类表达式,对其求值可以确定一个对象或函数的身份。
  • xvalue是指代资源可被复用的对象的glvalue(通常是因为该对象的生命周期即将结束)。
  • lvalue是不属于xvalue的glvalue。

问题解答

此处的x属于lvalue,调用移动构造是标准特殊规则的结果,和它的值类别无关。

  1. 首先明确变量名作为表达式的默认值类别:普通的自动变量名本身就是lvalue,你可以用编译期断言验证:在f()函数内加入static_assert(std::is_lvalue_reference_v<decltype((x))>);,编译会直接通过,说明x作为表达式对应的值类别就是lvalue。
  2. 移动构造被调用的原因:C++标准在拷贝消除相关章节做了特殊规定:当return语句的操作数是符合要求的自动存储期局部变量时,重载决议选择构造函数会优先把它当做右值处理,优先匹配移动构造,匹配失败才回退到拷贝构造。throw语句抛出局部变量时也有完全相同的规则。
  3. 它和xvalue定义的差异:xvalue的核心属性是「表达式本身就代表资源可复用」,比如std::move(x)的返回结果就是典型的xvalue。而这里的x本身没有xvalue属性,只是标准在return/throw的特定场景下许可了资源复用,属于规则层面的特殊例外,和xvalue的定义并不冲突。

内容的提问来源于stack exchange,提问作者Géry Ogam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 22:51:01