1. 内联函数的本质
c++ 中的 inline 有两个含义:
(1) 允许函数在多个翻译单元中重复定义
用于放宽 odr(one definition rule)。
→ 这是 inline 最重要的实际用途。
(2) 作为性能优化提示
告诉编译器“可以内联展开”。
→ 编译器可以忽略,不是强制。
2. 内联展开是什么?
通常函数调用包含跳转、压栈、返回等开销。
内联展开 = 把函数代码直接拷贝到调用点,避免调用开销。
示例:
inline int sqr(int x) { return x * x; }
展开后等价于直接写 x * x。
3. 编译器何时会内联?
编译器的优化器根据启发式决定:
会内联的典型情况:
- 函数很短(几行)
- 在头文件定义(可见函数体)
- 优化级别 o2 / o3
- constexpr、模板、类内定义成员(均“倾向内联”)
不会内联的情况:
- 递归函数
- 虚函数(除非去虚化)
- 大型函数(几十行以上)
- 启用 o0 无优化
- 编译器认为会导致代码膨胀(icache miss 风险)
4. inline 对链接的影响
普通函数只能在一个 .cpp 中定义,否则多重定义错误。
但 inline 函数可以这样:
// foo.h
inline int add(int a, int b) { return a + b; }
然后出现在多个 cpp 中,链接器会合并。
类内定义的函数默认 inline,因此类成员函数出现在头文件是合法的。
模板函数也是同理(模板必须写在头文件)。
5. inline vs 宏
| 对比项 | inline | 宏 |
|---|---|---|
| 类型检查 | 有 | 无 |
| 作用域 | 有 | 文本替换 |
| 调试支持 | 好 | 差 |
| 常见用途 | 小函数 | 简单表达式、条件编译 |
一般规则:能用 inline,就不要用宏。
6. inline 对性能的真实影响
正面:
- 消除函数调用成本(主要在小函数中有效)
- 触发更多优化(常量传播、死代码删除)
负面:
- 代码膨胀,可能让指令缓存命中率下降 → 性能变差
- 大函数内联通常是负优化
因此:
内联最有效的场景:频繁调用的小函数(数学计算、向量操作)。
(eigen、sophus、gtsam 都这么做)
7. 工程中内联的推荐模式
应当内联:
- 小函数(特别是数学运算)
- getter/setter
- 模板函数
- 类内定义成员函数
- 简单内存操作和判断逻辑
不要内联:
- 复杂流程函数(icp、优化残差、求导)
- 大循环
- 虚函数
- 调试时需要断点的复杂函数
8. 常见误区
误区 1:加 inline 就会内联
事实:编译器完全可以忽略。
误区 2:内联一定加速
事实:大函数内联反而变慢。
误区 3:inline 主要用来优化性能
事实:inline 的核心用途是允许头文件重复定义函数。
9. 精简总结
- inline = “允许多次定义” + “可选择性内联展开”
- 编译器才最终决定是否真正内联
- 用于头文件小函数非常合适
- 滥用会导致代码膨胀和性能下降
- 函数越短越可能被内联
10.c++ 内联函数综合示例
1.最常用:类内定义 = 默认内联
class vec3 {
public:
double x, y, z;
// 类内定义 → 默认 inline
double norm2() const {
return x * x + y * y + z * z;
}
void scale(double s) {
x *= s; y *= s; z *= s;
}
};特点:
- 类内定义的函数自动成为
inline - 在头文件中使用时不会导致 odr 冲突
- 常用于 getters/setters、小数学操作(slam 中大量使用)
2.类外定义 but 内联:适用于头文件工具函数
// vec_utils.h
#pragma once
inline double dot(const vec3& a, const vec3& b) {
return a.x*b.x + a.y*b.y + a.z*b.z;
}为什么要 inline?
因为头文件中定义函数,如果不 inline → 多重定义。
适合:数学工具函数、小型转换函数、小 lambda 替代。
3.模板函数天然 inline
slam & 点云处理中大量用的模板代码本质都是 inline:
template<typename t>
inline t clamp(t v, t lo, t hi) {
return (v < lo) ? lo : (v > hi) ? hi : v;
}
其实模板函数不写 inline 也能 inline,因为:
- 模板必须放头文件
- 每个 tu 都需要实例化
- 编译器会自动当成 inline 处理
4.内联 + constexpr(超轻量函数最佳组合)
constexpr inline double deg2rad(double deg) {
return deg * 3.14159265358979323846 / 180.0;
}
用途:
- 编译期数学常量
- 小型转换函数
- 角度/弧度转换(slam 必备)
5.内联 setter / getter
强烈推荐用于性能敏感代码(尤其是数学类型/几何类):
class pose {
public:
inline double x() const { return tx; }
inline void setx(double v) { tx = v; }
private:
double tx;
};现代 c++ get/set 可 inline 解决跳转开销。
6.内联 + forceinline 强制内联(gcc/clang/msvc)
对于高度性能敏感的代码(例如点云 icp 内循环):
#if defined(_msc_ver)
#define force_inline __forceinline
#else
#define force_inline __attribute__((always_inline)) inline
#endif
force_inline double sqr(double x) { return x * x; }注意:
- 滥用会导致代码膨胀(影响指令缓存)
- 建议用于极短(1~3 行)的小函数
7.与 static inline 结合用于 header-only 工具库
可避免 odr 冲突:
// math_utils.h
#pragma once
static inline double fast_min(double a, double b) {
return (a < b) ? a : b;
}推荐场景:
头文件单独工具函数,但不希望暴露全局符号。
8.类模板内联成员函数(eigen, sophus, slam 常用写法)
template<typename t>
class box {
private:
t min_, max_;
public:
inline t size() const { return max_ - min_; }
inline bool contains(const t& x) const { return x >= min_ && x <= max_; }
};典型用途:bounding box、se(3)、so(3) 数学类型等。
9.内联函数的真实性能对比(示例)
非内联版本(有函数调用开销)
double sqr(double x) { return x*x; }
double sum(const std::vector<double>& v) {
double s = 0;
for(double x : v) s += sqr(x);
return s;
}编译器可能无法展开(尤其跨文件编译时)。
内联版本(更易优化)
inline double sqr(double x) { return x*x; }
优势:
- 去掉调用开销
- 可进行循环展开 / simd 优化
- 能被进一步优化为
x*x的常量传播
10.完整示例:内联 + 模板 + 内联类成员
这是一个仿 slam 库的小型数学类型示例:
#pragma once
#include <cmath>
class vec3 {
public:
double x, y, z;
inline vec3(double x, double y, double z)
: x(x), y(y), z(z) {}
inline double dot(const vec3& o) const {
return x*o.x + y*o.y + z*o.z;
}
inline vec3 operator+(const vec3& o) const {
return vec3(x+o.x, y+o.y, z+o.z);
}
inline vec3 normalized() const {
double n = std::sqrt(dot(*this));
return vec3(x/n, y/n, z/n);
}
};
// 工具函数(header-only)
inline double distance(const vec3& a, const vec3& b) {
return (a + vec3(-b.x, -b.y, -b.z)).normalized().dot(a);
}这是 slam / icp / 点云算法中常见的数学类写法:
全都 inline,以消除函数开销,加速内循环。
小结:内联函数的最佳工程实践
| 场景 | 是否 inline |
|---|---|
| 类内部定义 | 自动 inline,推荐 |
| 头文件小工具函数 | 必须 inline |
| 模板函数 | 自动 inline, ok |
| 性能敏感数学函数 | inline 或 forceinline |
| 大型函数 | 不推荐(代码膨胀) |
| 接口函数(虚函数) | 不能 inline |
到此这篇关于c++ 内联函数(inline function)详解的文章就介绍到这了,更多相关c++ 内联函数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论