关于函数的潜在优化¶
1.返回值优化(RVO、NRVO)
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// 用于追踪对象生命周期的基类
class Tracer {
public:
Tracer(const std::string& name) : name_(name) {
std::cout << "[" << name_ << "] Default constructor @ " << this << "\n";
}
Tracer(const Tracer& other) : name_(other.name_ + "_copy") {
std::cout << "[" << name_ << "] Copy constructor from " << &other << " to "
<< this << "\n";
}
Tracer(Tracer&& other) noexcept : name_(std::move(other.name_) + "_moved") {
std::cout << "[" << name_ << "] Move constructor from " << &other << " to "
<< this << "\n";
}
~Tracer() { std::cout << "[" << name_ << "] Destructor @ " << this << "\n"; }
Tracer& operator=(const Tracer& other) {
if (this != &other) {
name_ = other.name_ + "_copy_assigned";
std::cout << "[" << name_ << "] Copy assignment from " << &other << " to "
<< this << "\n";
}
return *this;
}
Tracer& operator=(Tracer&& other) noexcept {
if (this != &other) {
name_ = std::move(other.name_) + "_move_assigned";
std::cout << "[" << name_ << "] Move assignment from " << &other << " to "
<< this << "\n";
}
return *this;
}
const std::string& name() const { return name_; }
private:
std::string name_;
};
// 不可拷贝也不可移动的类(C++17 之前无法返回这类对象)
class NonCopyableNonMovable {
public:
NonCopyableNonMovable(const std::string& id) : id_(id) {
std::cout << "[NonCopyableNonMovable] Constructor: " << id_ << " @ " << this
<< "\n";
}
~NonCopyableNonMovable() {
std::cout << "[NonCopyableNonMovable] Destructor: " << id_ << " @ " << this
<< "\n";
}
// 禁止拷贝和移动
NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
private:
std::string id_;
};
// 原始代码中的结构
struct LargeClass {
int i;
char c;
double d;
};
// 基本 RVO:返回临时对象(C++17 起强制拷贝消除)
auto rvoExample(int i, char c, double d) {
return LargeClass{i, c,
d}; // 不要在这里使用 std::move,会阻止编译器进行完全优化
}
// NRVO:返回命名对象(不是强制的,取决于编译器)
Tracer nrvoExample(const std::string& name) {
Tracer obj(name); // 命名局部对象
return obj; // 现代编译器通常会优化掉拷贝/移动
}
// 混合返回路径(可能阻止 NRVO)
Tracer conditionalReturn(bool flag) {
if (flag) {
return Tracer("temporary"); // 临时对象 - 更容易优化
} else {
Tracer named("named"); // 命名对象
return named; // 可能优化,取决于编译器
}
}
// 多返回点(现代编译器通常能处理)
Tracer multipleReturns(int value) {
if (value < 0) return Tracer("negative");
if (value == 0) return Tracer("zero");
return Tracer("positive"); // 所有路径返回相同类型
}
// C++17 强制拷贝消除示例:返回不可拷贝/不可移动对象
NonCopyableNonMovable createNonCopyableNonMovable() {
return NonCopyableNonMovable("uncopyable_object"); // C++17 前无法编译
}
// 无法应用 RVO 的情况:返回参数
Tracer returnParameter(Tracer param) {
// 通常无法对参数应用 RVO,因为 param 已经是现有对象
return param; // 会调用移动构造函数(如果有)或拷贝构造函数
}
// 无法应用 RVO 的情况:返回静态对象
Tracer& getStaticTracer() {
static Tracer instance("static_instance");
return instance; // 返回引用,不涉及 RVO
}
// 模板函数中的 RVO
template <typename T>
T createObject() {
return T(); // 模板中的 RVO,取决于实例化类型
}
// 返回 std::vector(大型对象)的示例
std::vector<int> createLargeVector() {
std::vector<int> vec(1000, 42); // 创建大向量
return vec; // NRVO 适用,或 C++11 后会使用移动语义
}
// 有副作用的计数器(注意:依赖拷贝副作用是危险的)
class Counter {
public:
Counter() { ++instance_count_; }
Counter(const Counter&) { ++instance_count_; }
~Counter() { --instance_count_; }
static int instance_count() { return instance_count_; }
private:
static int instance_count_;
};
int Counter::instance_count_ = 0;
Counter createCounter() {
return Counter(); // RVO 会影响 instance_count_ 的值
}
int main() {
std::cout << "===== 基本 RVO 示例 =====\n";
LargeClass lc_obj = rvoExample(10, 'c', 3.14);
std::cout << "\n===== NRVO 示例 =====\n";
Tracer nrvo_obj = nrvoExample("nrvo_test");
std::cout << "\n===== 条件返回示例 =====\n";
Tracer cond_obj1 = conditionalReturn(true);
Tracer cond_obj2 = conditionalReturn(false);
std::cout << "\n===== 多返回点示例 =====\n";
Tracer multi_obj1 = multipleReturns(-1);
Tracer multi_obj2 = multipleReturns(0);
Tracer multi_obj3 = multipleReturns(1);
std::cout << "\n===== C++17 强制拷贝消除示例 =====\n";
auto non_copyable = createNonCopyableNonMovable(); // C++17 前无法编译
std::cout << "\n===== 无法应用 RVO 的情况:返回参数 =====\n";
Tracer param_obj = returnParameter(Tracer("parameter"));
std::cout << "\n===== 无法应用 RVO 的情况:返回静态对象 =====\n";
Tracer& static_ref = getStaticTracer();
std::cout << "Static object name: " << static_ref.name() << "\n";
std::cout << "\n===== 模板函数中的 RVO =====\n";
auto template_obj = createObject<Tracer>();
std::cout << "\n===== 大型对象返回示例 =====\n";
auto large_vec = createLargeVector();
std::cout << "Vector size: " << large_vec.size() << "\n";
std::cout << "\n===== 依赖拷贝副作用的危险示例 =====\n";
{
Counter c1 = createCounter();
std::cout << "Counter instances after RVO: " << Counter::instance_count()
<< "\n";
}
// 演示强制拷贝(禁用 RVO)的情况
std::cout << "\n===== 禁用 RVO 的情况(如果编译时使用 "
"-fno-elide-constructors) =====\n";
Tracer forced_copy = Tracer(
"forced_copy"); // 如果使用 -fno-elide-constructors,这会产生额外拷贝
return 0;
}
2.纯函数(pure function)
- 没有副作用(side-effects) - 不会修改任何外部状态或全局变量
- 返回值仅依赖于输入参数 - 不依赖任何外部状态
- 对相同的输入,总是返回相同的结果 - 完全确定性行为
- 本质上遵循数学函数的概念
纯函数的特性使编译器能够进行多种高级优化,但是编译器识别纯函数有局限性
- 跨模块编译问题 - 当函数定义在不同模块或库中,编译器无法确定其是否为纯函数
- 缺乏标准化标注 - 没有跨编译器统一的方式来声明函数为纯函数
#ifdef __GNUC__
#define pure_function __attribute__((const))
#else
#define pure_function
#endif
double Func1(double) pure_function;
double Func2(double x) { return Func1(x) * Func1(x) + 1.; }
- GNU编译器: 会识别
const属性,只调用一次Func1 - 其他编译器: 由于没有标准属性,会调用两次
Func1
不同编译器的纯函数属性
| 编译器 | 属性/关键字 | 说明 |
|---|---|---|
| GCC/Clang | __attribute__((const)) |
严格纯函数,不访问内存 |
| GCC/Clang | __attribute__((pure)) |
函数不修改内存,但可能读取全局内存 |
| MSVC | __declspec(noalias) |
限制指针别名,部分相关 |
| C++11/14/17 | constexpr |
有限的编译时纯函数支持 |
| C++20 | [[nodiscard]] |
虽不直接标记纯函数,但可配合使用 |