我猜你在这里遗漏了一些东西。
静态函数?
将函数声明为静态将使其在其编译单元中“隐藏”。
具有命名空间范围 (3.3.6) 的名称如果是以下名称,则具有内部链接
——显式声明为静态的变量、函数或函数模板;
3.5/3 - C++14 (n3797)
当名称具有内部链接时,它所表示的实体可以被同一翻译单元中其他范围的名称引用。
3.5/2 - C++14 (n3797)
如果你在一个头文件中声明这个静态函数,那么包括这个头文件在内的所有编译单元都会有自己的函数副本。
问题是,如果该函数内有静态变量,则包括该头文件在内的每个编译单元也将有自己的个人版本。
内联函数?
将其声明为 inline 使其成为内联的候选对象(现在在 C++ 中这并不意味着很多,因为编译器会内联或不内联,有时会忽略关键字 inline 存在或不存在的事实):
带有 inline 说明符的函数声明(8.3.5、9.3、11.3)声明了一个内联函数。 inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。在调用点执行此内联替换不需要实现;但是,即使省略了这个内联替换,7.1.2 中定义的内联函数的其他规则仍应遵守。
7.1.2/2 - C++14 (n3797)
在头文件中,它有一个有趣的副作用:内联函数可以在同一个模块中定义多次,并且链接器将简单地将“它们”合并为一个(如果由于编译器的原因它们没有被内联)。
对于内部声明的静态变量,标准中明确规定了一个,而且只有一个:
extern 内联函数中的静态局部变量总是引用同一个对象。
7.1.2/4 - C++98/C++14 (n3797)
(函数默认为 extern,因此,除非您特别将函数标记为静态,否则这适用于该函数)
这具有“静态”的优点(即可以在标头中定义)而没有缺陷(如果没有内联,它最多存在一次)
静态局部变量?
静态局部变量没有链接(它们不能在其范围之外通过名称引用),但具有静态存储期限(即它是全局的,但其构造和销毁遵循特定规则)。
静态+内联?
混合 inline 和 static 将产生您所描述的后果(即使函数是内联的,内部的静态变量也不会,并且您将使用与编译单元一样多的静态变量结束,包括定义你的静态函数)。
回答作者的附加问题
自从我写了这个问题后,我就使用 Visual Studio 2008 进行了尝试。我尝试打开所有使 VS 符合标准的选项,但我可能错过了一些选项。结果如下:
当函数只是“内联”时,静态变量只有一个副本。
当函数是“静态内联”时,有多少个翻译单元就有多少个副本。
现在真正的问题是,事情是否应该如此,或者这是否是 Microsoft C++ 编译器的特性。
所以我想你有类似的东西:
void doSomething()
{
static int value ;
}
你必须意识到,函数内部的静态变量,简单地说,一个全局变量,除了函数的作用域外,对所有变量都隐藏,这意味着只有它在内部声明的函数才能到达它。
内联函数不会改变任何东西:
inline void doSomething()
{
static int value ;
}
只有一个隐藏的全局变量。编译器会尝试内联代码这一事实不会改变只有一个全局隐藏变量这一事实。
现在,如果你的函数被声明为静态的:
static void doSomething()
{
static int value ;
}
那么它对于每个编译单元都是“私有的”,这意味着每个 CPP 文件,包括声明静态函数的头文件,都将拥有它自己的函数私有副本,包括它自己的全局隐藏变量的私有副本,因此如有很多变量,因为有包括头在内的编译单元。
将“内联”添加到内部带有“静态”变量的“静态”函数:
inline static void doSomething()
{
static int value ;
}
就内部的静态变量而言,与不添加“inline”关键字的结果相同。
所以VC++的行为是正确的,你误会了“内联”和“静态”的真正含义。