内联函数中的静态variables
我有一个在头文件中声明和定义的函数。 这本身就是一个问题。 当这个函数没有被内联时,每个使用这个头部的翻译单元都会得到这个函数的一个副本,当它们被链接在一起的时候会被复制。 我“固定”,通过内联function,但恐怕这是一个脆弱的解决scheme,因为据我所知,编译器不保证内联,即使您指定“内联”关键字。 如果不是这样,请纠正我。
不pipe怎样,真正的问题是,这个函数里的静态variables会发生什么? 最终有多less份?
我想你在这里错过了一些东西。
静态function?
声明一个静态函数将使其在编译单元中“隐藏”。
名称空间范围(3.3.6)的名称具有内部链接,如果它是名称的话
– 显式声明为静态的variables,函数或函数模板;
3.5 / 3 – C ++ 14(n3797)
当名称具有内部链接时,它表示的实体可以被来自同一翻译单元中的其他范围的名称引用。
3.5 / 2 – C ++ 14(n3797)
如果你在一个头文件中声明了这个静态函数,那么包含这个头文件的所有编译单元都将拥有它自己的函数副本。
问题是,如果函数内部有静态variables,包含这个头文件的每个编译单元也会有自己的个人版本。
内联函数?
声明它内联使它成为内联的候选人(现在在C ++中并不意味着很多,因为编译器会内联或者不内联,有时会忽略关键字内联的存在或缺失):
带有内联说明符的函数声明(8.3.5,9.3,11.3)声明了一个内联函数。 内联说明符向实现指出,在调用点的函数体的内联replace优于通常的函数调用机制。 在调用点执行这个内联replace不需要实现; 但是,即使省略了这种内联replace,7.1.2中定义的内联函数的其他规则仍应遵守。
7.1.2 / 2 – C ++ 14(n3797)
在头文件中,它有一个有趣的副作用:内联函数可以在同一个模块中被多次定义,链接器将会简单地将它们连接成一个(如果由于编译器的原因而没有内联的话)。
对于里面声明的静态variables,标准专门说有一个,只有其中一个:
外部内联函数中的静态局部variables总是指向同一个对象。
7.1.2 / 4 – C ++ 98 / C ++ 14(n3797)
(函数默认是extern,所以,除非你明确地标记你的函数是静态的,这适用于那个函数)
这具有“静态”(即可以在头部定义)的优点,没有缺陷(如果不是内联,它最多只能存在一次)
静态局部variables?
静态局部variables没有链接(它们不能在范围外被名称引用),但具有静态存储时间(即全局的,但其构build和破坏遵循特定的规则)。
静态+内联?
然后混合内联和静态将会产生你所描述的结果(即使函数被内联,内部的静态variables也不会),并且你将以尽可能多的静态variables结束,因为你有编译单元,包括静态函数的定义)。
回答作者的附加问题
自从我写了这个问题以来,我试用了Visual Studio 2008。我尝试打开所有的选项,使得VS符合标准,但是有可能我错过了一些。 这是结果:
当函数只是“内联”时,只有一个静态variables的副本。
如果函数是“静态内联”,则有多less个翻译单元。
真正的问题是现在是否应该这样,或者如果这是Microsoft C ++编译器的特质。
所以我想你有这样的事情:
void doSomething() { static int value ; }
你必须认识到,函数内部的静态variables,简单地说,是一个全局variables,除了函数的作用域之外都是隐藏的,这意味着只有里面声明的函数才能达到它。
内联函数不会改变任何东西:
inline void doSomething() { static int value ; }
将只有一个隐藏的全局variables。 编译器将尝试内联代码的事实不会改变只有一个全局隐藏variables的事实。
现在,如果你的函数被声明为静态的:
static void doSomething() { static int value ; }
那么它对于每个编译单元都是“私有的”,这意味着每个包含声明了静态函数的头文件的CPP文件将具有它自己的函数的私有副本,包括它自己的全局隐藏variables的私有副本,有包括标题的编译单元。
添加“内联”到一个“静态”function与“静态”variables里面:
inline static void doSomething() { static int value ; }
与不添加这个“inline”关键字相同的结果,就内部的静态variables而言。
所以VC ++的行为是正确的,你错在“内联”和“静态”的真正含义。
我相信编译器创build了许多variables的副本,但是链接器select了一个,并使所有其他的引用。 当我尝试创build内联函数的不同版本时,我得到了类似的结果; 如果函数没有实际内联(debugging模式),则所有调用都会转到相同的函数,而不pipe它们是从哪个源文件调用的。
想像一个编译器的片刻 – 它怎么可能呢? 每个编译单元(源文件)都是独立的,可以单独编译; 每个人都必须创build一个variables的副本,认为这是唯一的一个。 链接器能够跨越这些边界并调整variables和函数的引用。
我发现Mark Ransom的答案很有帮助 – 编译器创build了许多静态variables的副本,但是链接器select一个,并在所有的翻译单元中执行。
在其他地方,我发现这一点:
请参阅[dcl.fct.spec] / 4
[..]具有外部链接的内联函数在所有翻译单元中应具有相同的地址。 外部内联函数中的静态局部variables总是指向同一个对象。 extern内联函数中的string文字是不同翻译单元中的同一个对象。
我没有一个标准的副本来检查,但它匹配我在VS Express 2008中检查程序集的经验
这应该是这样的。 “static”告诉编译器你希望函数在编译单元中是本地的,所以你希望每个编译单元有一个副本,并且每个实例的每个实例需要一个静态variables副本。
“inline”用来告诉编译器你想要内联函数; 现在,它只是把它看作是“好的,如果有几个代码副本,只要确保它是相同的function”。 所以每个人都分享静态variables。
注意:这个答案是为了回应自己贴出来的原帖而写的。
自从我写了这个问题以来,我试用了Visual Studio 2008。我尝试打开所有的选项,使得VS符合标准,但是我可能错过了一些。 这是结果:
当函数只是“内联”时,只有一个静态variables的副本。
如果函数是“静态内联”,则有多less个翻译单元。
真正的问题是现在是否应该这样,或者这是Microsoft C ++编译器的一个想法。
我相信你最终会得到一个翻译单位。 您已经有效地获得了该函数的许多版本(及其声明的静态variables),每个包含该头文件的翻译单元都有一个版本。
内联意味着可执行代码(指令)被内联到调用函数的代码中。 无论您是否要求,编译器都可以select这样做。 这对函数中声明的variables(数据)没有影响。
除了任何devise问题,这可能意味着,因为你已经坚持了它,你应该使用静态在这种情况下不内联。 这样每个人都有相同的variables。 (静态function)
静态意味着一个副本分布在整个程序中,但是内联意味着它在同一个程序中需要多次相同的代码,所以不可能在内联函数中创build一个静态variables。