为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C++
猛跑小猪 2019-08-06 15:00:26
为什么我不能在类中初始化非const静态成员或静态数组?为什么我不能在类中初始化非const 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]’我有两个问题:为什么我不能static在课堂上初始化数据成员?为什么我不能static在类中初始化数组,甚至是const数组?
查看完整描述

3 回答

?
Helenr

TA贡献1780条经验 获得超4个赞

为什么我不能static在课堂上初始化数据成员?

C ++标准只允许在类中初始化静态常量积分或枚举类型。这是a允许初始化而其他人没有的原因。

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

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

什么是整体类型?

C ++ 03 3.9.1基本类型
§7

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

脚注:

43)因此,列举(7.2)不是不可或缺的; 但是,枚举可以提升为int,unsigned int,long或unsigned long,如4.5中所述。

解决方法:

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

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整数可以被视为编译时常量。编译器知道整数值不会随时改变,因此它可以应用自己的魔法并应用优化,编译器只是内联这样的类成员,即它们不再存储在内存中,因为需要存储在内存中被删除,它给这些变量提供了Bjarne提到的规则的例外。

值得注意的是,即使static const整数值可以具有类内初始化,也不允许采用这些变量的地址。如果(并且仅当)它具有类外定义,则可以获取静态成员的地址。这进一步验证了上面的推理。

允许枚举这是因为枚举类型的值可以在期望int的位置使用。看上面的引文


这在C ++ 11中是如何变化的?

C ++ 11在一定程度上放宽了限制。

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

如果静态数据成员是const文字类型,则其在类定义中的声明可以指定一个大括号或大小为初始化器,其中作为赋值表达式的每个initializer子句都是一个常量表达式。可以在类定义中使用if 声明文字类型的静态数据成员,其声明应指定一个大括号或者等于初始化器,其中每个initializer子句都是赋值表达式constexpr specifier;是一个不变的表达。[注意:在这两种情况下,成员可能会出现在常量表达式中。-end note]如果在程序中使用该成员,并且命名空间作用域定义不包含初始化程序,则该成员仍应在命名空间作用域中定义。

此外,C ++ 11 允许(§12.6.2.8)在声明它(在其类中)的地方初始化非静态数据成员。这将意味着很容易的用户语义。

请注意,这些功能尚未在最新的gcc 4.7中实现,因此您可能仍会遇到编译错误。


查看完整回答
反对 回复 2019-08-06
?
GCT1015

TA贡献1827条经验 获得超4个赞

这似乎是旧时简单连接器的残留。您可以在静态方法中使用静态变量作为变通方法:

// 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;}

建立:

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)实际上足够智能。

搞笑:0123在手臂和3210x86上打印。


查看完整回答
反对 回复 2019-08-06
?
精慕HU

TA贡献1845条经验 获得超8个赞

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


查看完整回答
反对 回复 2019-08-06
  • 3 回答
  • 0 关注
  • 959 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信