在C++11中:
(1).声明为constexpr的函数隐式地是内联函数;
(2).deleted函数隐式地是一个内联函数。
在内联函数中:
1.所有函数定义中的函数局部静态对象(function-local static object)在所有翻译单元之间共享(它们都引用一个翻译单元中定义的同一对象)。
2.所有函数定义中定义的类型在所有翻译单元中也相同。
inline关键字的最初目的是向优化器指示函数的内联替换优先于函数调用,即不执行函数调用CPU指令将控制权转移到函数体,而是执行函数的副本即函数体在不生成调用的情况下执行。这避免了函数调用(传递参数并检索结果)产生的开销,但可能会导致更大的可执行文件,因为函数的代码必须重复多次。
由于关键字inline的含义是非绑定的(non-binding),因此编译器可以自由地对任何未标记为内联的函数使用内联替换,并且可以自由地生成对任何标记为内联的函数的函数调用。
C++17引入了内联变量,即允许在多个文件中定义的变量。内联变量的工作方式与内联函数类似,并且具有相同的要求(编译器必须能够在使用该变量的任何地方看到相同的完整定义)。
内联说明符(inline specifier)在具有静态存储持续时间(static storage duration)的变量(静态类成员或命名空间范围内的变量)的decl-specifier-seq(declaration specifiers)中使用时,将该变量声明为内联变量。
声明为constexpr的静态成员变量(但不是命名空间范围内的变量)隐式地是内联变量。
C++的两种链接方式:外部链接(External Linking)和内部链接(Internal Linking)
1.外部链接:函数或变量可以在其它翻译单元中使用。默认情况下,在C++中定义的函数和全局变量都具有外部链接属性。
2.内部链接:函数或变量只能在定义它的翻译单元中使用。
内联函数或内联变量(inline function or inline variable)具有以下属性:
1.内联函数或内联变量的定义必须在访问它的翻译单元(translation unit,源代码文件如.cpp)中可访问
2.具有外部链接(external linkage)(例如未声明为静态)的内联函数或内联变量具有以下附加属性:
(1).程序中可能存在多个内联函数或内联变量的定义,只要每个定义出现在不同的翻译单元中(different translation unit)并且(对于非静态内联函数和内联变量)所有定义都是相同的。例如,内联函数或内联变量可以定义在包含在多个源文件中的头文件中。
(2).它必须在每个翻译单元中声明为内联。
(3).每个翻译单元都有相同的地址(same address).
命名空间范围内的内联const变量默认具有外部链接(与非内联、非易失性、const限定(non-inline non-volatile const-qualified)变量不同)。
因为函数的关键字inline的含义开始意味着"允许多个定义"而不是"首选内联",所以该含义被扩展到内联变量。在现代C++中,内联已演变为"允许多个定义"的意思。
内联变量消除了将C++代码打包为仅含头文件库的主要障碍(Inline variables eliminate the main obstacle to packaging C++ code as header-only libraries)。
内联变量在未声明为静态时具有外部链接.
如果内联变量有多个定义,编译器会在需要时选择其中一个定义.
与内联函数不同,内联变量可以具有静态存储持续时间(static storage duration)。如果内联变量是在类范围内定义的,则它的行为类似于静态成员变量。此类成员变量可以在类内部初始化。
内联变量在所有翻译单元中具有相同的内存地址。
C++17允许在不同的翻译单元中对内联变量进行多个定义,并且每个翻译单元将拥有自己的变量副本。
内联变量默认具有外部链接。如果我们想定义一个具有内部链接的内联变量,我们可以使用静态说明符。
当我们在全局变量上使用inline关键字时,我们不需要在其他地方将它们定义为"extern"。对于静态成员变量,我们不需要在类外部定义它们。
根据一次定义原则(ODR, One Definition Rule),一个变量或实体的定义只能出现在一个编译单元内----除非该变量或实体被定义为inline的。
内联变量和thread_local:通过使用thread_local你可以为每个线程创建一个内联变量。
注意:
(1).如果具有外部链接的内联函数或内联变量在不同的翻译单元中定义不同,则程序格式错误(ill-formed),无需诊断。
(2).内联说明符不能与块作用域(在另一个函数内)的函数或变量声明一起使用。
(3).内联说明符无法重新声明已在翻译单元中定义为非内联的函数或非内联的变量。
(4).隐式生成的(implicitly-generated)成员函数以及在其第一个声明中声明为默认的(defaulted)任何成员函数都是内联的,就像类定义中定义的任何其他函数一样。
(5).如果在不同的翻译单元中声明内联函数,则每个翻译单元末尾的累积默认参数集必须相同(the accumulated sets of default arguments must be the same at the end of each translation unit)。
以下为测试代码:
1. inline.hpp:此头文件会被多个.cpp文件include,若不带inline,会编译不过
class InlineVariable {
public:
inline static int var{ 10 }; // 不带inline,则只能在类外初始化;即使在类外初始化,如果被多个cpp文件包含也会error;带inline后,即使被多个cpp文件包含也OK
const char* csdn_addr{ "https://blog.csdn.net/fengbingchun/" };
static constexpr int num{ 6 }; // 对于静态成员,constexpr修饰符隐含着inline,等价于: inline static constexpr int num{ 6 };
inline static std::string name{ "Messy_Test" }; // 整个程序中只有一个
inline static thread_local int count{ 1 }; // 每个线程有一个
std::string city{ "BeiJing" }; // 每个实例有一个
};
inline InlineVariable inline_variable; // 不带inline,则会报重复定义;带inline后,即使被多个cpp文件包含也OK
inline thread_local InlineVariable inline_variable2; // 每个线程一个对象
2. inline.cpp:
int test_inline_variable_1()
{
std::cout << "var: " << InlineVariable::var << "\n";
std::cout << "csdn addr: " << inline_variable.csdn_addr << "\n";
std::cout << "num: " << InlineVariable::num << "\n";
return 0;
}
执行结果如下图所示:
GitHub:https://github.com/fengbingchun/Messy_Test