为什么“内联”静态不允许,除了整数?

时间:2009-12-15 12:47:53

标签: c++ static initialization class-members

  

可能重复
  Why can't I have a non-integral static const member in a class?

struct Example
{
    static const int One = 1000; // Legal
    static const short Two = 2000; // Illegal
    static const float Three = 2000.0f; // Illegal
    static const double Four = 3000.0; // Illegal
    static const string Five = "Hello"; // Illegal
};

#2,#3,#4和#5是否有任何违法原因?

我想我知道#5的原因:编译器需要一个“真正的”字符串对象(因为它不是内置类型)并且不能盲目地将Five替换为"Hello",就好像它是#define Five "Hello"。但是,如果是这种情况,编译器是否不能在.obj文件中留下提示并告诉链接器在某处自动创建string Five的一个实例?

对于#3和#4,特别是#2(哈哈!)......我真的看不出任何可能的原因!浮点数和双打数是内置类型,就像int一样!而short只是一个(可能)较短的整数。


编辑:我正在使用Visual Studio 2008进行编译。我认为在这种情况下所有编译器的行为都相同,但显然g ++编译得很好(#5除外)。 VS为这些片段提供的错误是:

    error C2864: 'Example::Two' : only static const integral data members can be initialized within a class
    error C2864: 'Example::Three' : only static const integral data members can be initialized within a class
    error C2864: 'Example::Four' : only static const integral data members can be initialized within a class
    error C2864: 'Example::Five' : only static const integral data members can be initialized within a class

8 个答案:

答案 0 :(得分:7)

int和short是合法的,如果你的编译器不允许它们,那么你的编译器就会破灭:

  

9.4.2 / 4:...如果静态数据成员是const整数或const   枚举类型,其声明在   类定义可以指定一个   常量初始化器,它应是一个整数常量表达式。

我认为浮点数和双精度的原因并没有特别作为C ++标准中的常量处理,就像整数类型一样,C ++标准是谨慎的,因为float和double上的算术运算可能是微妙的在编译机上不同于在执行代码的机器上。为了让编译器像(a + b)那样计算常量表达式,它需要得到运行时得到的相同答案。

对于整数而言,这不是一个问题 - 如果它不同,你可以相对便宜地模拟整数运算。但是,编译器在目标设备上模拟浮点硬件可能非常困难。如果有不同版本的芯片并且编译器不知道代码将在哪个上运行,那甚至可能是不可能的。甚至在你开始搞乱IEEE舍入模式之前就已经存在了。因此标准避免了它,因此它不必定义编译时评估何时以及如何与运行时评估不同。

正如Brian所提到的,C ++ 0x将使用constexpr来解决这个问题。如果我对原始动机是正确的,那么大概10年就足以解决指定这些问题的困难。

答案 1 :(得分:4)

Example::OneExample::Two都应该为您编译,并且它们确实在您所声明的相同环境中为我编译(VS 2008)。

我不相信Example::ThreeExample::Four应该在标准C ++中编译,但我认为有一个gcc扩展允许它。 Example::Five不应该编译。

您可以在结构声明之后初始化它们,通常在源文件中:

const float Example::Three = 2000.0f;
const double Example::Four = 3000.0;
const string Example::Five = "Hello";

这是最便携的方式,即使您的编译器允许您在声明中定义Example::ThreeExample::Four,我也建议您这样做。

另一种选择是简单地从相同类型的静态函数返回值。

struct Example
{
    //...
    static double Four() { return  = 3000.0; }
    //...
};

This answer也讨论了可能的原因 This answer讨论了即将推出的C ++标准如何通过constexpr

提供帮助

答案 2 :(得分:1)

  

在C ++ 98中,只有静态const成员   可以初始化整数类型   在课堂上,初始化者必须   是一个不变的表达。这些   限制确保我们可以做到   在编译时初始化。

请参阅In-class member initializers

  

§9.4.2静态数据成员

     

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

答案 3 :(得分:1)

#1和2符合标准。不幸的是,有些编译器根本不符合。这就是为什么,例如,Boost设计师不得不引入像BOOST_STATIC_CONSTANT这样烦人的宏来生成可移植的库。如果您不想在.cpp文件中定义常量,则可移植的解决方法是使用enum。虽然,显然在这种情况下你不能保证类型,你不能使用浮点数。

答案 4 :(得分:0)

在VS2008下,我收到以下错误:

1>.\Weapon Identification SystemDlg.cpp(223) : error C2864: 'Example::Three' : only static const integral data members can be initialized within a class
1>.\Weapon Identification SystemDlg.cpp(224) : error C2864: 'Example::Four' : only static const integral data members can be initialized within a class
1>.\Weapon Identification SystemDlg.cpp(225) : error C2864: 'Example::Five' : only static const integral data members can be initialized within a class

很糟糕,但我想如果你的编译器拒绝,你就不得不这样做......我不知道这是一个规范的东西,但我确定有人会纠正我......

答案 5 :(得分:0)

重新浮点初始化器,C ++ 98规范就是这样说的(5.19):

  

浮动文字只有在转换为整数或枚举类型时才会出现。

答案 6 :(得分:0)

正如其他人所发现的那样,C ++标准禁止使用浮点值初始化静态const成员。

至少据我所知,这有一个相当简单的原因。有一种感觉(至少部分证明)应该允许一个实现动态调整浮点精度,所以直到运行时才可能实现知道将从特定浮点生成/将要生成的确切浮点值文字。事实上,甚至有可能在执行期间发生变化。

此功能确实存在于真实硬件中。例如,Intel x86在浮点控制寄存器中有几个位,用于控制浮点计算的精度。默认情况下,计算是在80位长双精度类型上完成的,并且根据请求仅舍入到64位双精度或32位浮点数。寄存器中的这些位可以在执行期间修改,因此(例如)“1.23”在一个地方可以将变量初始化为一个值,而在程序的另一部分中的“1.23”(在调整精度之后)可能会导致(稍微)不同的值。

至少据我所知,至少在大多数典型的机器上,这仍然是理论上的可能性。尽管英特尔硬件允许动态调整FP精度,但我不知道在翻译FP文字时尝试将这种调整考虑在内的任何编译器(甚至不是英特尔)(尽管英特尔的编译器至少支持80位长)双重型)。

答案 7 :(得分:0)

正如其他人所指出的那样,在某些情况下你的编译器会被破坏。但我从来没有真正理解为什么它不允许浮点类型,除了“标准这样说”。似乎没有好的技术原因。