%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
%%
# 拷贝省略 Copy Elision
[拷贝省略(Copy Elision)](https://en.cppreference.com/w/cpp/language/copy_elision)是 C++中的一个优化技术,允许编译器在某些情况下**省略临时对象的拷贝和移动操作**,旨在避免不必要的资源分配与释放,提升程序执行效率。
- 在 C++17 之前,拷贝省略属于 "编译器" 自身的优化行为,例如 RVO/NRVO 优化。
- 自 C++17 起,对拷贝省略的规则进行了**扩展和明确化**,将某些情况的拷贝省略**纳入了标准规定**(即从 "**编译器可选**" 变为 "**强制/编译器必须**"),**从 "语义标准" 的层面上保证了拷贝省略的行为**,而不再是仅依赖于编译器自身的优化策略。
> [!caution]
> - 在 **`return` 语句**或 **` throw ` 表达式**中,如果满足或将满足拷贝省略的触发条件,但编译器因为某些原因不能执行拷贝省略,则**编译器将尝试使用==移动构造函数==** (除非源对象是函数参数)
> >
>
> - 在**常量表达式**(constant expression)或**常量初始化**(constant initialization)中,不会执行拷贝省略。
<br>
## 强制省略(mandatory copy elision)
自 C++17 起,**纯右值 (`prvalue`) 直到需要使用时才会被具现化(materialized)**,并直接**构造到其最终所处的上下文的内存空间中**。
因此,在以下几个涉及 "**==纯右值语义==**"(prvalue semantics) 的场景中,C++17 标准要求编译器**必须/强制执行拷贝省略**:
- **函数返回一个==临时对象==** 时(仅 **URVO** 被标准规定为必须执行,而不包含 NRVO,后者仍是可选的)
- `return MyClass();`
- **==临时对象==用于初始化时**:用临时对象(`prvalue`)初始化 "**同类型的**另一个对象"时,省略拷贝/移动构造函数。
- `MyClass obj = MyClass();`
- **==临时对象==用作函数形参时**:将临时对象(`prvalue`)作为函数参数传递时,省略拷贝/移动构造函数。
- `SomeFunc(MyClass());`
<br>
## 非强制性拷贝省略(non-mandatory copy/move elision)
在其他场景下,**"拷贝省略" 仍然是编译器可选的**,C++标准不强制要求。
包括:
- **NRVO 优化**
- 在 `throw` 表达式中,当操作数是 **`non-volatile` 的自动变量**,且其并非函数参数、catch 子句参数、且其作用域没有扩展到最内层的 `try` 语句块时(如果存在 `try` 语句块);
- 在 catch 子句中,当**参数与抛出的异常对象的类型相同**(忽略 cv-qualification) 时,**对==异常对象的拷贝==将被省略**。
- catch 子句体中,将直接访问异常对象,就像通过引用捕获一样。
- 在协程中... since C++20
<br><br><br>
# RVO 返回值优化
**==返回值优化==** (Return Value Optimization,RVO),是编译器提供的一种**针对 "函数返回值"** 的编译层面的优化[^1]。
返回值优化发生在 "函数需要**返回一个==函数体内==构建的、==与函数返回类型相同==的==局部对象==**" 的场景。
编译器执行返回值优化时,**将直接在 "==调用者期望的目标位置==" 构造 "将返回的对象"**,从而**省略中间的任何移动、拷贝构造**。
> [!caution] 函数形参与此无关!当返回函数形参时,其不能被进行优化
编译器提供的 **返回值优化** (Return Value Optimization,RVO)包括两种:
- **==URVO== 优化**(Unnamed Return Value Optimization):未命名的返回值优化
- **==NRVO== 优化**(Named Return Value Optimization):具名的返回值优化
> [!info] URVO 与 NRVO 的区别
> URVO 优化与 NRVO 优化的区别只在于返回值 "**是否为 ==具名(具有标识符)的局部左值对象==** ":
> - 如果 `return` 语句**直接返回一个临时对象 (`prvalue`)**,则属于 **==URVO== 优化**;
> - 如果 `return` 语句返回一个**具名的局部变量**,则属于 **==NRVO== 优化**。
>
> [!NOTE] **RVO 优化**是**编译器本身提供的功能**,在 C++11 标准之前就已经被大多数编译器支持,**其与移动语义无关**,**不依赖于移动构造函数**。
> [!caution] 在满足触发 `RVO` 优化的场景下,应用 `std::move()` 是多余的!
>
> C++标准关于 RVO 的部分表明,**如果满足 RVO 的条件**,由于某种原因 **编译器未能应用 RVO 优化**,则返回的对象 **==必须被视为右值==**。
> 实际上,**标准要求当 RVO 被允许时**,**要么实行拷贝消除,要么==将 `std::move` 隐式应用==于返回的局部对象**。
>
> ```cpp
> MyClass createObject() {
> MyClass obj;
> //...
> return obj; // 如果编译器未执行RVO优化, 则会等价于隐式地应用下列语句. 因此, 显式使用`std::move()` 是多余的.
> // return std::move(obj);
> }
> ```
>
>
> [!info] 可以将 RVO 优化关闭,可以对 g++增加选项 `-fno-elide-constructors`
>
<br>
### 返回值优化的原理
这一优化是编译器**在编译阶段**实现的,而非运行时的动态决策。
编译器在进行代码分析后,若确定可进行返回值优化,则**在生成代码时将==更改返回对象的存储位置==**——**直接在 "==调用函数的上下文==" 中分配该对象的存储空间**,而不是在函数的局部作用域内。由此,函数体内构造的、**将返回的局部对象实际位于==函数调用者的栈帧上==**。具体过程为:
- **在==调用者的栈帧==上分配空间**:编译器会在函数调用者的栈帧上**预留空间用于构造返回对象**;
- **调整返回指令**:编译器调整函数的返回指令,以确保不会移动或拷贝对象,而是**直接使用预留空间的地址**。
经过这一底层优化,编译器确保当函数返回时,**不需要再通过调用拷贝/移动构造函数来创建一个新的对象副本**。
<br>
### 返回值优化触发场景
URVO 触发场景:直接返回**临时对象**
```cpp title:rvo_exam.cpp
MyClass createObject() {
//...
return MyClass(); // 可以应用URVO, 编译器直接在返回值的位置构造obj, 省略拷贝/移动构造.
}
```
NRVO 触发场景:返回具名的**局部对象**
```cpp title:nrvo_exam.cpp
MyClass createObject() {
MyClass obj; // 具名的局部对象
//...
return obj; // 可以应用NRVO, 编译器直接在返回值的位置构造obj, 省略拷贝/移动构造.
}
```
URVO/NRVO **==不适用==** 的场景:返回**函数的参数**
> [!caution] RVO/NRVO 不适用的返回场景
>
> 函数直接返回 "**传入的形参对象**" 时,不适用 URVO/NRVO,将**会调用拷贝构造函数**。
>
> ```cpp title: no_rvo. cpp
> // 函数直接返回 "传入的形参", 不适用 URVO/NRVO,会调用拷贝构造函数。
> MyClass func1 (MyClass obj) { // 该函数将会触发两次"拷贝构造函数"的调用: 传参时, 函数返回时;
> return obj;
> }
>
> MyClass func2 (const MyClass& obj) { // 该函数将会触发一次"拷贝构造函数"的调用: 函数返回值时;
> return obj;
> }
> ```
>
>
<br><br><br>
# 常量传播(Const Propagation)
**常量传播(Const Propagation)** 是由编译器提供的 "**编译优化技术**",其作用是:
- **将程序中==已知的常量值==替换到表达式中。**
- **消除不必要的变量,==减少内存访问==,提高程序执行效率。**
简单来说,**如果某个变量的值在编译时可以确定为一个常量**,编译器就会**在所有使用该变量的地方直接替换这个常量值**,从而减少不必要的变量访问和计算。
![[02-开发笔记/01-cpp/类与对象/cpp-类的数据成员#^mku4i3]]
> [!NOTE]
> ![[_attachment/02-开发笔记/01-cpp/编译器相关/cpp-编译器优化.assets/IMG-cpp-编译器优化-C3D14BDF4DE15C038345B65CB2A79A8F.png|464]]
<br><br><br>
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
# ♾️参考资料
# Footnotes
[^1]: [C++ 函数返回对象时并没有调用拷贝构造函数](https://blog.csdn.net/nbu_dahe/article/details/119142610)