转:容易被误解的inline
C++中的inline是个容易被误解的关键字,即使是专家也常会犯错。
不信?请打开《Thinking in C++ 2rd Edition Volumn 1》,找到Chapter 9 "Inline Function"的Exercise 6:
“Prove that inline functions default to internal linkage."
为什么我说这里作者犯错了呢?因为C++ 03标准中角标79处明确写到:
“ The inline keyword has no effect on the linkage of a function.“
看,标准明确表示inline关键字不对函数的linkage产生影响,作者却要读者试图证明inline function默认为internal linkage!
关于inline,我觉得最好的参考还是水木CplusPlus版的FAQ
http://www.newsmth.net/bbscon.php?bid=335&id=146496
以下内容基本来自于该FAQ,并加入了自己的一些理解和解释
误解一:inline函数没有单独的函数体,也不能取地址。
事实:inline修饰并不会改变函数的通常语义,仍可通过函数指针调用:
误解二:inline和virtual不能同时修饰一个函数。
事实:inline和virtual并不冲突,可以同时修饰函数
误解三:virtual函数即使声明为inline,由于是late binding,无法判断实际调用的版本,编译器也无法展开。
事实:虚函数调用并不总是late binding;late binding发生在通过指针或引用来调用函数的时候,在其它情况下理论上是可能对 virtual inline的函数进行展开的
误解四: inline函数一定是internal linkage/no linkage的。
inline 与函数的linkage无关。inline函数同样可以用static和extern修饰,并具备同一般函数相同的linkage。标准要求 external linkage的inline函数在所有编译单元中具有相同的地址。external linkage的inline函数内定义的静态变量同样应在所有编译单元中表现为单一对象,具有相同的地址。
这一条估计是最让人困惑的特 性了。因为通常inline函数的定义都放在头文件中,习惯了C++头文件使用规定的人都自然的将inline函数视为internal linkage。“如果是external linkage的话,该头文件被多个源文件包含的话,就会出现多次定义的错误了啊!”
对于正常的函数,这是正确的——头文件中应该包含函数的声明而不是定义。但是inline function 在这个问题上被特别对待。
TC++PL 9.2中写道:"一个inline函数必须在需要使用它的每个编译单元(TU)里定义——通过完全一样的定义"
C++ 03 [7.1.2.4]中对应的文字:
"An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (3.2).If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.
An inline function with external linkage shall have the same address in all translation units.
A static local variable in an extern inline function always refers to the same object.
A string literal in anextern inline function is the same object in different translation units."
注意上面用红色引用的这两条要求,这两条翻译成大白话就是:“我保证external linkage的inline function在最终的可运行程序中只有一份实体,所以你就放心的在头文件中包含inline function的定义吧!“
所以,要分清楚事情的因果关系:不能因为头文件中可以包含inline function的定义,就想当然的认定inline function默认情况下是internal linkage。真相是相反的,因为标准为external linkage的 inline function 提供了特殊待遇,才能在头文件中包含inline function的定义。
一定要注意:这两条特殊待遇只对external linkage的inline function有效,对internal linkage的inline function是无效的
C++03中的[3.2.3]和[3.2.5] 也提供了与inline function的一些约定
最后是一个简单的示例程序,可以很好的用于区分概念
external inline function
不信?请打开《Thinking in C++ 2rd Edition Volumn 1》,找到Chapter 9 "Inline Function"的Exercise 6:
“Prove that inline functions default to internal linkage."
为什么我说这里作者犯错了呢?因为C++ 03标准中角标79处明确写到:
“ The inline keyword has no effect on the linkage of a function.“
看,标准明确表示inline关键字不对函数的linkage产生影响,作者却要读者试图证明inline function默认为internal linkage!
关于inline,我觉得最好的参考还是水木CplusPlus版的FAQ
http://www.newsmth.net/bbscon.php?bid=335&id=146496
以下内容基本来自于该FAQ,并加入了自己的一些理解和解释
误解一:inline函数没有单独的函数体,也不能取地址。
事实:inline修饰并不会改变函数的通常语义,仍可通过函数指针调用:
误解二:inline和virtual不能同时修饰一个函数。
事实:inline和virtual并不冲突,可以同时修饰函数
误解三:virtual函数即使声明为inline,由于是late binding,无法判断实际调用的版本,编译器也无法展开。
事实:虚函数调用并不总是late binding;late binding发生在通过指针或引用来调用函数的时候,在其它情况下理论上是可能对 virtual inline的函数进行展开的
误解四: inline函数一定是internal linkage/no linkage的。
inline 与函数的linkage无关。inline函数同样可以用static和extern修饰,并具备同一般函数相同的linkage。标准要求 external linkage的inline函数在所有编译单元中具有相同的地址。external linkage的inline函数内定义的静态变量同样应在所有编译单元中表现为单一对象,具有相同的地址。
这一条估计是最让人困惑的特 性了。因为通常inline函数的定义都放在头文件中,习惯了C++头文件使用规定的人都自然的将inline函数视为internal linkage。“如果是external linkage的话,该头文件被多个源文件包含的话,就会出现多次定义的错误了啊!”
对于正常的函数,这是正确的——头文件中应该包含函数的声明而不是定义。但是inline function 在这个问题上被特别对待。
TC++PL 9.2中写道:"一个inline函数必须在需要使用它的每个编译单元(TU)里定义——通过完全一样的定义"
C++ 03 [7.1.2.4]中对应的文字:
"An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (3.2).If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.
An inline function with external linkage shall have the same address in all translation units.
A static local variable in an extern inline function always refers to the same object.
A string literal in anextern inline function is the same object in different translation units."
注意上面用红色引用的这两条要求,这两条翻译成大白话就是:“我保证external linkage的inline function在最终的可运行程序中只有一份实体,所以你就放心的在头文件中包含inline function的定义吧!“
所以,要分清楚事情的因果关系:不能因为头文件中可以包含inline function的定义,就想当然的认定inline function默认情况下是internal linkage。真相是相反的,因为标准为external linkage的 inline function 提供了特殊待遇,才能在头文件中包含inline function的定义。
一定要注意:这两条特殊待遇只对external linkage的inline function有效,对internal linkage的inline function是无效的
C++03中的[3.2.3]和[3.2.5] 也提供了与inline function的一些约定
最后是一个简单的示例程序,可以很好的用于区分概念
// a.h
inline int eif(){
static int n=0;
return ++n;
}
static inline int iif(){
static int n=0;
return ++n;
}
static int nif() {
static int n=0;
return ++n;
}
int eig();
int iig();
int nig();
inline int eif(){
static int n=0;
return ++n;
}
static inline int iif(){
static int n=0;
return ++n;
}
static int nif() {
static int n=0;
return ++n;
}
int eig();
int iig();
int nig();
// a.cpp
#include "a.h"
int eig() { return eif(); }
int iig() {return iif();}
int nig() {return nif();}
#include "a.h"
int eig() { return eif(); }
int iig() {return iif();}
int nig() {return nif();}
//main.cpp
#include "a.h"
#include <iostream>
using namespace std;
int main() {
cout<<"external inline function ";
cout<<eig()<<' ';
cout<<eif()<<' ';
cout<<"internal inline function ";
cout<<iig()<<' ';
cout<<iif()<<' ';
cout<<"non-inline function ";
cout<<nig()<<' ';
cout<<nif()<<' ';
return 0;
}
#include "a.h"
#include <iostream>
using namespace std;
int main() {
cout<<"external inline function ";
cout<<eig()<<' ';
cout<<eif()<<' ';
cout<<"internal inline function ";
cout<<iig()<<' ';
cout<<iif()<<' ';
cout<<"non-inline function ";
cout<<nig()<<' ';
cout<<nif()<<' ';
return 0;
}
1
2
internal inline function
1
1
non-inline function
1
1
2
internal inline function
1
1
non-inline function
1
1