1) 左值、右值、纯右值(prvalue)、将亡值(xvalue)
面试答案要点
- 
左值(lvalue):在表达式结束后仍然存在、可取地址的对象(有名字或持久存储)。
 - 
右值(rvalue):不能持久存在、一般不可取地址的临时值。包含两类:
- 
纯右值(prvalue) :字面量、计算结果等,代表"值";如
42,x + y,std::string("a")(C++17 起 prvalue 直接初始化目标对象)。 - 
将亡值(xvalue) :即将被销毁但仍可"窃取资源"的对象表达式;典型来源
std::move(x)、返回std::string&&的函数结果等。 
 - 
 - 
右值引用(T&&) 绑定右值(尤其是 xvalue),为移动语义提供基础。
 
演示代码
#include <iostream>
#include <string>
#include <utility>
int main() {
    std::string s = "hello"; // s 是左值
    auto&& a = std::string("tmp"); // std::string("tmp") 是 prvalue,直接构造成临时对象,再被 a(万能引用)绑定
    auto&& b = std::move(s);       // std::move(s) 是 xvalue(将亡值)
    // std::cout << "s: " << s << "\n"; // 此时 s 的状态未定义,访问它是不安全的,尽管在某些实现中它可能是空的
    std::cout << "a: " << a << "\n";
    std::cout << "b: " << b << "\n"; // b 绑定的是 s 的将亡值(资源移动后 b 拥有)
}
        关键点 :std::move(s) 不是"移动",而是把 s 标记为可被移动(转为 xvalue)。
2) T&, const T&, T&& 的绑定规则
面试答案要点(最常考表)
- 
T&:只能绑定左值。 - 
const T&:能绑定左值 也能绑定右值(延长右值临时对象生命周期)。 - 
T&&:只能绑定右值(prvalue/xvalue)。 - 
模板形参
T&&(推导场景 )是转发引用/万能引用:- 
实参是左值 ⇒
T推导为T&,形参折叠成T& &&⇒ T& - 
实参是右值 ⇒
T推导为T,形参为 T&& 
 - 
 
演示代码
#include <iostream>
#include <type_traits>
void bind_l(int& )       { std::cout << "int&\n"; }
void bind_cl(const int& ) { std::cout << "const int&\n"; }
void bind_r(int&& )       { std::cout << "int&&\n"; }
template <typename T>
void probe(T&& x) { // 转发(万能)引用
    if constexpr (std::is_lvalue_reference_v<T&&>) std::cout << "probe: lvalue\n";
    else                                           std::cout << "probe: rvalue\n";
}
int main() {
    int i = 0;
    bind_l(i);       // OK: 左值
    bind_cl(i);      // OK
    // bind_r(i);    // ❌ 左值不能绑定到 int&&
    bind_r(42);      // OK: 右值
    const int ci = 1;
    bind_cl(2);      // OK: 右值绑定 const 引用
    // bind_l(2);    // ❌
    // bind_r(ci);   // ❌ const 左值不是右值
    probe(i);        // lvalue
    probe(3);        // rvalue
}
        3) std::move 与 std::forward 的区别
面试答案要点
- 
std::move(x):无条件 把表达式转成T&&(xvalue)。常用于"我确定要把资源移走"。 - 
std::forward<T>(x):有条件 转发:当模板实参T为左值引用时保持左值,为非引用时保持右值;用于完美转发。 - 
使用场景:
- 
写类的移动构造/赋值 ⇒ 用
std::move。 - 
写转发包装器/工厂函数 ⇒ 用
std::forward。 
 - 
 
演示代码
#include <iostream>
#include <string>
#include <utility>
void take(const std::string& s) { std::cout << "take(const&): " << s << "\n"; }
void take(std::string&& s)      { std::cout << "take(&&): "     << s << "\n"; }
template <typename T>
void wrapper_move(T&& x) {
    // 无论传左值还是右值,都被move成右值
    take(std::move(x));
}
template <typename T>
void wrapper_fwd(T&& x) {
    // 保留实参值类别(左值仍左值,右值仍右值)
    take(std::forward<T>(x));
}
int main() {
    std::string s = "hi";
    wrapper_move(s);             // 强行走 && 版本
    wrapper_fwd(s);              // 保持左值 => 走 const&
    wrapper_fwd(std::string("tmp")); // 右值 => 走 &&
}
        4) 移动构造 与 拷贝构造 的优先级
面试答案要点
- 
函数重载决议中:能移动就不拷贝(当参数是右值且存在可行移动构造时,会优先选择移动构造)。
 - 
若用户显式声明拷贝构造而未 声明移动构造,某些情况下编译器不会合成移动构造,导致右值也走拷贝。
 - 
为了启用移动,通常显式声明:
A(A&&) noexcept和A& operator=(A&&) noexcept。 - 
对含资源成员(如
std::unique_ptr、容器)移动能显著优化性能。 
演示代码
#include <iostream>
#include <vector>
struct A {
    std::vector<int> data;
    A() { std::cout << "A()\n"; }
    A(const A&) { std::cout << "A(const A&)\n"; }
    A(A&&) noexcept { std::cout << "A(A&&)\n"; }           // 移动构造
    A& operator=(const A&) { std::cout << "copy=\n"; return *this; }
    A& operator=(A&&) noexcept { std::cout << "move=\n"; return *this; }
};
A makeA() { A a; return a; }
int main() {
    A a1;
    A a2 = makeA();       // C++17 多为 RVO(见下一节);若未RVO,优先调用 A(A&&)
    A a3 = std::move(a1); // 明确右值 => 调用 A(A&&)
}
        5) RVO(返回值优化)与移动优化
面试答案要点
- 
RVO(Return Value Optimization) :编译器直接在调用方栈/存储上就地构造返回对象,省去拷贝/移动。
 - 
C++17 保证性 RVO :
return T{...};/return obj;(局部同名对象)在某些情形下强制省略拷贝/移动(即使定义了移动构造,也不会调用)。 - 
如果不能做(NRVO 失败),才会退回到移动(若可)或拷贝。
 - 
实践总结 :写返回值时尽量直接构造返回对象 ,不要为了"优化"而手工
std::move(returnObj)(反而可能禁用 NRVO)。 
演示代码(观察是否打印 Move/Copy)
#include <iostream>
struct Big {
    Big() { std::cout << "Big()\n"; }
    Big(const Big&) { std::cout << "Copy\n"; }
    Big(Big&&) noexcept { std::cout << "Move\n"; }
};
Big make1() {
    return Big(); // C++17: 保证性RVO,通常不打印 Copy/Move
}
Big make2() {
    Big b;
    return b;     // C++17: 多数实现做 NRVO;若失败,则优先 Move
}
int main() {
    std::cout << "make1 call:\n";
    Big x = make1();
    std::cout << "make2 call:\n";
    Big y = make2();
}
        要点 :不要写 return std::move(b);------这会把 b 变成 xvalue,禁用 NRVO,迫使调用移动构造。
小结速背(面试 30 秒版)
- 
左值可取址、持久存在;右值是临时值。右值含 prvalue 与 xvalue (如
std::move)。 - 
绑定规则:
T&仅左值;const T&左/右都行;T&&仅右值;模板T&&是万能引用。 - 
move:无条件右值化;forward:保持实参的值类别(完美转发)。 - 
右值优先走移动;未声明移动可能退化成拷贝。移动函数请标
noexcept。 - 
C++17:RVO/NRVO 常直接省略拷贝/移动;不要 对返回局部用
std::move。