我猜你在这里遗漏了一些东西。
静态函数?
将函数声明为静态将使其“隐藏”在其编译单元中。
具有命名空间范围 (3.3.6) 的名称具有内部链接,如果它是
— 显式声明为静态的变量、函数或函数模板;
3.5/3 - C++14 (n3797)
当名称具有内部链接时,它所表示的实体可以被同一翻译单元中其他范围的名称引用。
3.5/2 - C++14 (n3797)
如果您在标头中声明此静态函数,则包括此标头的所有编译单元都将拥有自己的该函数的副本。
问题是,如果该函数内部有静态变量,则包含此标头的每个编译单元也将有自己的个人版本。
内联函数?
将其声明为内联使其成为内联的候选者(现在在 C++ 中这没有多大意义,因为编译器会内联或不内联,有时会忽略关键字 inline 存在或不存在的事实):
带有内联说明符的函数声明(8.3.5、9.3、11.3)声明内联函数。内联说明符向实现表明,在调用时函数体的内联替换优先于通常的函数调用机制。不需要实现在调用时执行此内联替换;然而,即使省略此内联替换,仍应遵守 7.1.2 定义的内联函数的其他规则。
7.1.2/2 - C++14 (n3797)
在标头中,它有一个有趣的副作用:内联函数可以在同一模块中定义多次,链接器将简单地将“它们”连接成一个(如果由于编译器的原因没有内联它们)。
对于内部声明的静态变量,标准明确规定有一个,而且只有一个:
外部内联函数中的静态局部变量始终引用同一个对象。
7.1.2/4 - C++98/C++14 (n3797)
(默认情况下,函数是外部的,因此,除非您专门将函数标记为静态,否则这适用于该函数)
这具有“静态”的优点(即它可以在标头中定义)而没有缺陷(如果不内联,它最多存在一次)
静态局部变量?
静态局部变量没有链接(它们不能在其范围之外通过名称引用),但具有静态存储持续时间(即它是全局的,但其构造和销毁遵循特定规则)。
静态+内联?
混合内联和静态将产生您所描述的后果(即使函数是内联的,内部的静态变量也不会是内联的,并且您将以与编译单元一样多的静态变量结束,包括静态函数的定义)。
回答作者的补充问题
自从我写下这个问题后,我就用 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++的行为是正确的,你误解了“内联”和“静态”的真正含义。