%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # `inline` 说明符 `inline` 说明符有两个用途 [^1]: 1. **显式标记一个==静态存储持续性==的变量为"==内联变量=="**; 2. **显式标记一个函数为"==内联函数=="**; 除通过 `inline` 显式声明以外,也有**隐式的内联函数和内联变量**(参见后文)。 > [!caution] > > - `inline` 说明符不能被用于块作用域下的函数或变量声明 > - `inline` 说明符不能 **重声明 (re-declare)** 一个已在翻译单元中**被定义为 non-inline 的函数或变量**。 > <br><br> ## 内联函数、内联变量的性质 > 参见[^1] - 内联函数与变量的 "**==定义==**" **必须对 "每一个调用了该内联函数 or 使用了该内联变量的翻译单元" 可见**,编译器需要具此完成**内联替换**。 - 因此,**==内联函数、内联变量的定义==** 通常放置在头文件中,供所需使用的源文件引入。 - **试图在头文件中进行 `inline` 函数声明,而在源文件中进行函数定义是不可行的**。 - 内联函数与变量具有 "**==外部链接性==**",但**其定义可以存在于多个翻译单元**中,**只要所有定义均==保持一致==即可**,不会导致 "**重复定义**" 错误。 - 遵循**一体化定义规则**(One Definition Rule,**ODR**),要求所有翻译单元中出现的**定义必须完全一致**。 - 链接器会忽略重复的定义,只保留一个。 - 内联函数与变量尽管可存在多份定义,但 **==只会有一份实体==**,**被引入了其定义的==所有翻译单元所共享==** (在每个翻译单元中具有相同的地址,即所有翻译单元共同引用同一实体) - **内联函数作用域内**的 **==局部静态变量==** 也被 **所有翻译单元所共享**。 > [!NOTE] 一体化定义规则(One Definition Rule,ODR) > > C++ 规定: > > - **普通外部链接性函数:** 在整个程序中**只能有一个定义**。多个定义将导致**链接错误**。 > - **内联函数:** 可以在多个翻译单元中有**相同的定义**,但所有定义必须**完全相同**。链接器会**合并所有的相同定义**,保留一个有效的函数定义。 <br><br> # 内联函数 `inline` 关键字标识的函数为 "**内联函数**",该关键字的作用是 **==指示==(而非强制)** 编译器使用 "**==函数的内联替换==**" 来替代 "**函数调用**"。 "**内联替换**" 是指**在编译时将 "==函数调用语句==" 直接替换为 "==函数体内执行代码=="**,从而 **==省略==/不产生函数调用过程**: - 可**省去函数调用开销**(如栈帧的创建销毁、参数传递等); - 但**会增大编译生成的程序体积**(函数体代码在每个函数调用处都被重复地内嵌)。 > [!info] 除 `inline` 显式标识的内联函数外,还有 "隐式内联函数" > [!caution] `inline` 只是个**提示**,**编译器会根据其自身的优化选择来决定是否实际执行"内联替换"**: > > - 编译器**可以忽略 `inline` 声明**,不为内联函数执行内联替换,例如函数体较大或涉及复杂的控制结构(如循环和递归)等。 > - 编译器**可以为未标记 `inline` 的函数**在编译时使用内联替换进行优化; > > > *Since this meaning of the keyword inline is non-binding,* > > - *compilers are free to use inline substitution for any function that's not marked inline,* > > - *and are free to generate function calls to any function marked inline.* > <br> ### 内联函数的特性 ![[#内联函数、内联变量的性质]] <br> ### 隐式内联函数 "**隐式内联函数**" 是指未使用 `inline` 显式声明,但**默认具有 `inline` 特性**的函数。 以下函数均为 **==隐式内联函数==**: - 类中的下列函数: - **在类定义内部直接定义的任何函数**(包括成员函数和非成员函数,例如类定义中完整定义的友元函数) - 除非被附加到一个具名模块(since C++20) - **隐式生成的成员函数**(例如编译器生成的默认构造函数等) - **在首次声明时声明为 `= default` 的任何成员函数** - **在首次声明时==声明为`constexpr`== 或 `consteval` 的函数** - **声明为 `= deleted` 的函数**(其 `deleted` 定义可以出现在多个翻译单元中) <br><br> # 内联变量(since C++17) 以下情况声明一个**内联变量**(inline variable): - (1)**显式声明为 `inline`** 的 **静态存储持续性** 变量(静态成员变量、命名空间作用域变量) - `inline` 全局变量、`inline const` 全局常量、`inline constexpr` 全局常量 - `inline` 函数中的 **`static` 静态局部变量** - 类中的 `inline` 静态成员变量、 `constexpr` 静态成员变量 - (2)**在首次声明中==声明为 `constexpr`== 的静态成员变量**(隐式内联变量) > [!NOTE] 内联变量具有 "**外部链接性**",包括 `inline` 全局变量、`inline const` 或 `inline constexpr` 全局常量等 <br> ### 内联变量的作用 内联变量消除了将 c++代码打包为 "**header-only 库**" 的主要障碍: - C++17 之前,**全局变量**、**类的静态成员变量** 只能在全局作用域下进行一次性定义,由于具有 "**外部链接性**",因此通常只能在 `.cpp` 中定义。 - **如果放在头文件中,头文件被多个源文件包含后会触导致 "重复定义" 错误**。 - C++17 引入了 "**内联变量**" 后,**使用 ==`inline` 关键字==**,可以将全局变量或静态成员变量都**直接放在头文件中定义**。所有引入该头文件的翻译单元都将**持有一份自己的内联变量 =="定义"==**,**==而不会导致重定义问题==**,同时 **==所有翻译单元中的变量共享同一个实体==**。 > [!summary] > > - 头文件中定义的 **静态全局变量** 为**内部链接性**,每个引用该头文件的翻译单元中 **"独立"持有一份该变量的实体**,互不相同。 > - 头文件中定义的 "**内联变量**" 具有 **==外部链接性==**,在整个程序中只有一份实体,**被所有引入该头文件的翻译单元所共享**: > [!caution] `inline` 静态数据成员必须在 "==类内部==" 进行定义&初始化,不能在类外定义&初始化。 > > ```cpp > // my.h > class MyClass { > public: > // static int stcMemberVar = 5; // Error: 不允许对静态数据成员类内初始化 > static int stcMemberVar; > static inline int inlineStcMemberVar = 15; // allowed(仅允许类内初始值方式) > }; > > int MyClass::stcMemberVar = 5; // 静态数据成员类外初始化. 该语句需要放在`.cpp`中, 若放在头文件中会导致 "重复定义". > // inline int MyClass::inlineStcMemberVar = 15; // Error: 不允许类外定义. > ``` > <br> ### 内联变量的性质 ![[#内联函数、内联变量的性质]] ### 使用示例 示例一:在头文件中定义 "**全局内联变量**" ```cpp title:inline_variable.h #ifndef INLINE_VARIABLE_H #define INLINE_VARIABLE_H inline int globalInlineVar = 42; // 外部链接性, 但内联变量允许多个翻译单元包含, 不会导致重复定义错误, 且共享同一变量实体. inline const int globalInlineConst = 55; // 外部链接性, 同上. #endif ``` 示例二:在头文件中定义 "**类的静态内联数据成员**"(**==必须且只能在类内定义和初始化==**) ```cpp class Config { public: static inline const int MaxConnections = 100; // 常量 static inline double Pi = 3.14159; // 可变值 }; ``` <br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later <br> # ♾️参考资料 # Footnotes [^1]: [inline specifier - cppreference.com](https://en.cppreference.com/w/cpp/language/inline)