4

可能重复
为什么我不能在一个类中有一个非整数静态常量成员?

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 为该片段提供的错误是:

    错误 C2864:“Example::Two”:只能在类中初始化静态 const 整数数据成员
    错误 C2864:'Example::Three':只能在类中初始化静态 const 整数数据成员
    错误 C2864:'Example::Four':只能在类中初始化静态 const 整数数据成员
    错误 C2864:'Example::Five':只能在类中初始化静态 const 整数数据成员
4

8 回答 8

7

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

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

我相信浮点数和双精度数在 C++ 标准中没有像整数类型那样被特别视为常量的原因是,C++ 标准对浮点数和双精度数的算术运算在编译机,而不是它们在执行代码的机器上。为了让编译器评估像 (a + b) 这样的常量表达式,它需要得到与运行时相同的答案。

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

正如 Brian 提到的,C++0x 将使用constexpr. 如果我对最初的动机是正确的,那么大概 10 年的时间足以解决指定这些东西的困难。

于 2009-12-15T13:11:05.983 回答
4

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

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

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

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

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

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

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

这个答案也讨论了一个可能的原因。
这个答案讨论了即将到来的 C++ 标准将如何通过 constexpr 提供帮助

于 2009-12-15T12:54:38.990 回答
1

在 C++98 中,只能在类中初始化整型的静态 const 成员,并且初始化器必须是常量表达式。这些限制确保我们可以在编译时进行初始化。

请参阅类内成员初始化程序

§9.4.2 静态数据成员

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

于 2009-12-15T12:58:53.053 回答
1

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

于 2009-12-15T13:04:37.460 回答
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

这很糟糕,但我想如果你的编译器也拒绝,你就不必这样做......我不知道这是规范的事情,但我相信有人会纠正我......

于 2009-12-15T12:59:17.207 回答
0

关于浮点初始值设定项,C++98 规范有这样的说法(5.19):

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

于 2009-12-15T13:22:07.910 回答
0

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

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

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

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

于 2009-12-15T15:08:51.157 回答
0

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

于 2009-12-15T22:56:30.270 回答