为什么我不能在类中初始化非常量静态成员或静态数组?

为什么我不能在类中初始化非常量static成员或static数组?

 class A { static const int a = 3; static int b = 3; static const int c[2] = { 1, 2 }; static int d[2] = { 1, 2 }; }; int main() { A a; return 0; } 

编译器会发出以下错误:

 g++ main.cpp main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member 'b' main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before '{' token main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type 'const int [2]' main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before '{' token main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type 'int [2]' 

我有两个问题:

  1. 为什么我不能在类中初始化static数据成员?
  2. 为什么我不能在类中初始化static数组,甚至是const数组?

为什么我不能在类中初始化static数据成员?

C ++标准只允许在类内初始化静态常量积分或枚举types。 这是允许被初始化的原因,而其他的则不然。

参考:
C ++ 03 9.4.2静态数据成员
§4

如果静态数据成员是const整型或常量枚举types的,则它在类定义中的声明可以指定一个常数初始值设定项,它应该是一个整型常量expression式(5.19)。 在这种情况下,成员可以出现在整型常量expression式中。 如果在程序中使用成员,则该成员仍应在名称空间范围中定义,并且名称空间作用域定义不应包含初始值设定项。

什么是整型?

C ++ 03 3.9.1基本types
§7

typesbool,char,wchar_t以及有符号和无符号整数types统称为整型.43)整型的同义词是整型。

脚注:

因此,列举(7.2)并不是完整的; 然而,按照4.5的规定,枚举可以被提升为int,unsigned int,long或unsigned long。

解决方法:

您可以使用枚举技巧来初始化您的类定义中的数组。

 class A { static const int a = 3; enum { arrsize = 2 }; static const int c[arrsize] = { 1, 2 }; }; 

为什么标准不允许这样做?

Bjarne 在这里恰当地解释这一点

一个类通常在头文件中声明,而头文件通常包含在许多翻译单元中。 但是,为了避免复杂的链接器规则,C ++要求每个对象都有唯一的定义。 如果C ++允许将需要作为对象存储在内存中的实体的类定义中断,那么该规则将被破坏。

为什么只有static const整型和枚举允许在类初始化?

答案隐藏在Bjarne的报价中仔细阅读,
“C ++要求每个对象都有一个唯一的定义,如果C ++允许实体的类内定义需要作为对象存储在内存中,那么这个规则将被打破。

请注意,只有static const整数可以被视为编译时间常量。 编译器知道整数值不会随时改变,因此它可以应用自己的魔术和应用优化,编译器只是内联这样的类成员,即它们不再存储在内存中,因为被存储在内存中的需要被删除,它给了这样的variablesBjarne提到的规则的例外。

值得注意的是,即使static const整型值可以具有类内初始化,也不允许使用这些variables的地址。 如果(并且只有)它具有一个不符合类的定义,则可以采用静态成员的地址。这进一步validation了上面的推理。

枚举是允许的,因为枚举types的值可以在需要整数的地方使用。 见上面的引文


C ++ 11如何改变?

C ++ 11放松了一定的限制。

C ++ 11 9.4.2静态数据成员
§3

如果一个静态数据成员是const的常量types,它在类定义中的声明可以指定一个括号或等于初始值设定项 ,其中每个作为赋值expression式的 初始化子句都是一个常量expression式。 文字types的静态数据成员可以在类定义中用constexpr specifier; 如果是的话,它的声明应该指定一个括号或者等于初始值设定项 ,其中每个作为赋值expression式的 初始化子句都是一个常量expression式。 [注意:在这两种情况下,成员都可以出现在常量expression式中。 -end note]如果在程序中使用成员,名称空间作用域定义不包含初始值设定项,则该成员仍应在名称空间作用域中定义。

另外,C ++ 11 允许(第12.6.2.8节)非静态数据成员在声明的位置(在其类中)进行初始化。 这将意味着很容易的用户语义。

请注意,这些function还没有在最新的gcc 4.7中实现,所以你可能仍然会遇到编译错误。

这似乎是从简单的连接器的旧时代遗留下来的。 您可以在静态方法中使用静态variables作为解决方法:

 // header.hxx #include <vector> class Class { public: static std::vector<int> & replacement_for_initialized_static_non_const_variable() { static std::vector<int> Static {42, 0, 1900, 1998}; return Static; } }; int compilation_unit_a(); 

 // compilation_unit_a.cxx #include "header.hxx" int compilation_unit_a() { return Class::replacement_for_initialized_static_non_const_variable()[1]++; } 

 // main.cxx #include "header.hxx" #include <iostream> int main() { std::cout << compilation_unit_a() << Class::replacement_for_initialized_static_non_const_variable()[1]++ << compilation_unit_a() << Class::replacement_for_initialized_static_non_const_variable()[1]++ << std::endl; } 

build立:

 g++ -std=gnu++0x -save-temps=obj -c compilation_unit_a.cxx g++ -std=gnu++0x -o main main.cxx compilation_unit_a.o 

跑:

 ./main 

这个工作的原理(即使类定义包含在不同的编译单元中也是如此)表明,今天的链接器(gcc 4.9.2)确实足够聪明。

滑稽:在arm上打印0123和在x86上打印3210

我认为这是为了防止混淆声明和定义。 (考虑如果将文件包含在多个位置中可能会发生的问题)。

静态variables是特定于一个类的。 构造函数为一个实例初始化属性ESPECIALY。