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

发布于 2024-08-14 07:11:45 字数 1402 浏览 7 评论 0原文

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

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" ,就好像它是 #定义五个“你好”。但如果是这种情况,编译器不能在 .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

Possible Duplicate
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
};

Is there any reason for which #2, #3, #4 and #5 are illegal?

I think I know the reason for #5: the compiler needs a "real" string object (since it's not a built in type) and cannot mindlessy replace Five with "Hello" as if it was #define Five "Hello". But if that's the case, can't the compiler leave an hint in the .obj files and tell the linker to automatically create one instance of string Five somewhere?

For #3 and #4 and especially #2 (lol!)... I can't really see any possible reason! Floats and doubles are built-in types, just as int is! And short is just a (possibly) shorter integer.


EDIT: I'm using Visual Studio 2008 to compile it. I thought all compilers behaved the same in this case, but apparently g++ compiles that fine (except #5). The errors VS gives for that snippets are:

    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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

铃予 2024-08-21 07:11:45

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

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

我相信,在 C++ 标准中,浮点型和双精度型在 C++ 标准中没有像整型那样被特殊处理为常量,原因是 C++ 标准很谨慎,认为浮点型和双精度型的算术运算在不同类型上可能略有不同。编译机,而不是执行代码的机器。为了让编译器计算像 (a + b) 这样的常量表达式,它需要得到与运行时得到的相同的答案。

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

正如 Brian 提到的,C++0x 将通过 constexpr 来解决这个问题。如果我最初的动机是正确的,那么大概 10 年的时间就足以解决指定这些东西的困难。

The int and the short are legal, and if your compiler doesn't allow them then your compiler is bust:

9.4.2/4: ... If the static data member is of const integral or const
enumeration type, its declaration in
the class definition can specify a
constant-initializer which shall be an integral constant expression.

I believe that the reason that floats and doubles aren't treated specially as constants in the C++ standard, in the way that integral types are, is that the C++ standard is wary that the arithmetic operations on float and double could be subtly different on the compiling machine, than they are on the machine that executes the code. For the compiler to evaluate a constant expression like (a + b), it needs to get the same answer that the runtime would get.

This isn't so much of an issue with ints - you can emulate integer arithmetic relatively cheaply if it differs. But for the compiler to emulate floating-point hardware on the target device might be very difficult. It might even be impossible, if there are different versions of the chip and the compiler doesn't know which the code will run on. And that's even before you start messing with the IEEE rounding mode. So the standard avoided requiring it, so that it didn't have to define when and how compile-time evaluation can differ from runtime evaluation.

As Brian mentions, C++0x is going to address this with constexpr. If I'm right about the original motivation, then presumably 10 years has been long enough to work through the difficulties in specifying this stuff.

淤浪 2024-08-21 07:11:45

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::Three声明中的Example::Four

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

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

这个答案还讨论了可能的原因。

这个答案 讨论即将推出的 C++ 标准将如何通过 constexpr 提供帮助

Both Example::One and Example::Two should compile for you, and they do indeed compile for me in the same environment you stated (VS 2008).

I don't believe Example::Three, and Example::Four should compile at all in standard C++, but I think there is a gcc extension that allows it. Example::Five should not compile.

You can initialize them like so after the struct declaration, typically in your source file:

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

This is the most portable way to do it, and the way I would recommend doing it even if your compiler allows you to define Example::Three and Example::Four in your declaration.

Another option would be to simply return the value from a static function of the same type.

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

This answer discusses a possible reason as well.
This answer discusses how the upcoming C++ standard will help via constexpr

音栖息无 2024-08-21 07:11:45

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

#1 and 2 are compliant with the standard. Unfortunately, some compilers simply don't conform. That's why, for example, the Boost designers had to introduce annoying macros like BOOST_STATIC_CONSTANT to generate portable libraries. If you don't want to define the constant in a .cpp file, a portable workaround is to use an enum. Although, obviously in that case you have no guarantee about the type, and you can't use floats.

老子叫无熙 2024-08-21 07:11:45

在 C++98 中,只有静态 const 成员
整型可以被初始化
在类中,初始化器必须
是一个常量表达式。这些
限制确保我们能够做到
编译时初始化。

请参阅类内成员初始值设定项

§9.4.2 静态数据成员

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

In C++98, only static const members of
integral types can be initialized
in-class, and the initializer has to
be a constant expression. These
restrictions ensure that we can do the
initialization at compile-time.

See In-class member initializers.

§9.4.2 Static data members

If a static data member is of const integral or const enumeration type, its declaration in the class definition can
specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear
in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and
the namespace scope definition shall not contain an initializer.

对你而言 2024-08-21 07:11:45

在 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

它很糟糕,但我想如果你的编译器也拒绝,你就不必这样做......我不知道这是一个规范问题,但我确信有人会纠正我......

Under VS2008 I get the following error:

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

It sucks but I guess you just have to NOT do it if your compiler refuses too ... I'm not aware of this being a spec thing but im sure someone will correct me ...

马蹄踏│碎落叶 2024-08-21 07:11:45

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

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

Re the floating point initializers, the C++98 spec has this to say (5.19):

Floating literals can appear only if they are cast to integral or enumeration types.

风蛊 2024-08-21 07:11:45

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

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

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

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

As others have found, the C++ standard forbids initializing a static const member with a floating point value.

At least as I understand it, there's a fairly simple reason for this. There's a feeling (at least partially justified) that an implementation should be allowed to adjust the floating point precision dynamically, so it might not be until runtime that the implementation knows the exact floating point value that would/will be produced from a particular floating point literal. In fact, it's even possible that this could change during execution.

This capability does exist in real hardware. Just for example, the Intel x86 has a couple of bits in the floating point control register that control the accuracy of floating point calculations. By default, calculations are done on the 80-bit long double type, and only rounded to something like a 64-bit double or 32-bit float upon request. These bits in the register can be modified during execution, so (for example) "1.23" in one place could initialize a variable to one value, while "1.23" in another part of the program (after the precision had been adjusted) could result in a (slightly) different value.

At least as far as I know, this remains a theoretical possibility, at least on most typical machines. Although the Intel hardware allows dynamic adjustment of FP precision, I don't know of any compiler (not even Intel's) that attempts to take such an adjustment into account when translating FP literals (though Intel's compiler does at least support an 80-bit long double type).

冷情 2024-08-21 07:11:45

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

As others have pointed out, your compiler is broken in some cases. But I have never really understood the reason why it is not allowed for floating-point types, other than "The standard says so". There appears to be no good technical reason.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文