C++ 为什么我不能在一个类中有一个非整数静态 const 成员?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/370283/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't I have a non-integral static const member in a class?
提问by Jeffrey Martinez
I noticed C++ will not compile the following:
我注意到 C++ 不会编译以下内容:
class No_Good {
static double const d = 1.0;
};
However it will happily allow a variation where the double is changed to an int, unsigned, or any integral type:
但是,它很乐意允许将 double 更改为 int、unsigned 或任何整数类型的变体:
class Happy_Times {
static unsigned const u = 1;
};
My solution was to alter it to read:
我的解决方案是将其更改为:
class Now_Good {
static double d() { return 1.0; }
};
and figure that the compiler will be smart enough to inline where necessary... but it left me curious.
并认为编译器将足够聪明,可以在必要时内联……但这让我感到好奇。
Why would the C++ designer(s) allow me to static const an int or unsigned, but not a double?
为什么 C++ 设计者允许我静态 const 一个 int 或 unsigned,而不是一个 double?
Edit: I am using visual studio 7.1 (.net 2003) on Windows XP.
编辑:我在 Windows XP 上使用 Visual Studio 7.1 (.net 2003)。
Edit2:
编辑2:
Question has been answered, but for completion, the error I was seeing:
问题已得到解答,但为了完成,我看到了错误:
error C2864: 'd' : only const static integral data members can be initialized inside a class or struct
采纳答案by Adam Rosenfield
The problem is that with an integer, the compiler usuallydoesn't have to ever create a memory address for the constant. It doesn't exist at runtime, and every use of it gets inlined into the surrounding code. It can still decide to give it a memory location - if its address is ever taken (or if it's passed by const reference to a function), that it must. In order to give it an address, it needs to be defined in some translation unit. And in that case, you need to separate the declaration from the definition, since otherwise it would get defined in multiple translation units.
问题在于,对于整数,编译器通常不必为常量创建内存地址。它在运行时不存在,每次使用它都会内联到周围的代码中。它仍然可以决定给它一个内存位置 - 如果它的地址曾经被占用(或者如果它通过一个函数的 const 引用传递),它必须这样做。为了给它一个地址,它需要在某个翻译单元中定义。在这种情况下,您需要将声明与定义分开,否则它将在多个翻译单元中定义。
Using g++ with no optimization (-O0
), it automatically inlines constant integer variables but not constant double values. At higher optimization levels (e.g. -O1
), it inlines constant doubles. Thus, the following code compiles at -O1
but NOT at -O0
:
使用没有优化 ( -O0
) 的g++ ,它会自动内联常量整数变量而不是常量双精度值。在更高的优化级别(例如-O1
),它内联常量双精度。因此,以下代码编译 at-O1
但不编译-O0
:
// File a.h
class X
{
public:
static const double d = 1.0;
};
void foo(void);
// File a.cc
#include <stdio.h>
#include "a.h"
int main(void)
{
foo();
printf("%g\n", X::d);
return 0;
}
// File b.cc
#include <stdio.h>
#include "a.h"
void foo(void)
{
printf("foo: %g\n", X::d);
}
Command line:
命令行:
g++ a.cc b.cc -O0 -o a # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a # Succeeds
For maximal portability, you should declare your constants in header files and define them once in some source file. With no optimization, this will not hurt performance, since you're not optimizing anyways, but with optimizations enabled, this can hurt performance, since the compiler can no longer inline those constants into other source files, unless you enable "whole program optimization".
为了最大的可移植性,您应该在头文件中声明您的常量,并在某个源文件中定义它们一次。没有优化,这不会损害性能,因为您无论如何都没有优化,但是启用优化,这可能会损害性能,因为编译器不能再将这些常量内联到其他源文件中,除非您启用“整个程序优化” .
回答by Johannes Schaub - litb
I see no technical reason why
我看不出技术上的原因
struct type {
static const double value = 3.14;
};
is forbidden. Any occasion you find where it works is due to non-portable implementation defined features. They also seem to be of only limited use. For integral constants initialized in class definitions, you can use them and pass them to templates as non-type arguments, and use them as the size of array dimensions. But you can't do so for floating point constants. Allowing floating point template parameters would bring its own set of rules not really worth the trouble.
是禁止的。您发现它在哪里工作的任何场合都是由于非便携式实现定义的功能。它们似乎也只有有限的用途。对于在类定义中初始化的整数常量,您可以使用它们并将它们作为非类型参数传递给模板,并将它们用作数组维度的大小。但是对于浮点常量你不能这样做。允许浮点模板参数会带来它自己的一套规则,不值得麻烦。
Nonetheless, the next C++ version will allow that using constexpr
:
尽管如此,下一个 C++ 版本将允许使用constexpr
:
struct type {
static constexpr double value = 3.14;
static constexpr double value_as_function() { return 3.14; }
};
And will make type::value
a constant expression. In the meantime, your best bet is to follow the pattern also used by std::numeric_limits
:
并且会做出type::value
一个恒定的表达。同时,您最好的选择是遵循 也使用的模式std::numeric_limits
:
struct type {
static double value() { return 3.14; }
};
It will not return a constant expression (value is not known at compile time), but that only matters theoretical, since practical the value will be inlined anyway. See the constexprproposal. It contains
它不会返回一个常量表达式(在编译时值是未知的),但这只是理论上的,因为实际上该值无论如何都会被内联。请参阅constexpr提案。它包含了
4.4
Floating-point constant expressions
Traditionally, evaluation of ?oating-point constant expression at compile-time is a thorny issue. For uniformity and generality, we suggest to allow constant-expression data of ?oating point types, initialized with any ?oating-point constant expressions. That will also increase compatibility with C99 [ISO99, §6.6] which allows
[#5] An expression that evaluates to a constant is required in several contexts. If a ?oating expression is evaluated in the translation envi- ronment, the arithmetic precision and range shall be at least as great as if the expression were being evaluated in the execution environ- ment.
4.4
Floating-point constant expressions
传统上,在编译时计算浮点常量表达式是一个棘手的问题。为了统一性和通用性,我们建议允许浮点类型的常量表达式数据,用任何浮点常量表达式初始化。这也将增加与 C99 [ISO99, §6.6] 的兼容性,这允许
[#5] 在几个上下文中需要一个计算为常量的表达式。如果在翻译环境中计算浮动表达式,则算术精度和范围应至少与在执行环境中计算表达式一样大。
回答by Michael Burr
It doesn't really give a rationale, but here's what Stroustrup has to say about this in "The C++ Programming Language Third Edition":
它并没有真正给出一个基本原理,但这是 Stroustrup 在“C++ 编程语言第三版”中对此的看法:
10.4.6.2 Member Constants
It is also possible to initialize a static integral constant member by adding a constant-expressioninitializer to its member declaration. For example:
class Curious { static const int c1 = 7; // ok, but remember definition static int c2 = 11; // error: not const const int c3 = 13; // error: not static static const int c4 = f(17); // error: in-class initializer not constant static const float c5 = 7.0; // error: in-class not integral // ... };
However, an initialized member must still be (uniquely) defined somewhere, and the initializer may not be repeated:
const int Curious::c1; // necessary, but don't repeat initializer here
I consider this a misfeature. When you need a symbolic constant within a class declaration, use an enumerator (4.8, 14.4.6, 15.3). For example:
class X { enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 }; // ... };
In that way, no member definition is needed elsewhere, and you are not tempted to declare variables, floating-point numbers, etc.
10.4.6.2 成员常量
也可以通过 在成员声明中添加一个常量表达式初始化器来初始化一个静态整型常量成员。例如:
class Curious { static const int c1 = 7; // ok, but remember definition static int c2 = 11; // error: not const const int c3 = 13; // error: not static static const int c4 = f(17); // error: in-class initializer not constant static const float c5 = 7.0; // error: in-class not integral // ... };
但是,初始化成员仍然必须(唯一)在某处定义,并且初始化器不能重复:
const int Curious::c1; // necessary, but don't repeat initializer here
我认为这是一个错误的功能。当您需要在类声明中使用符号常量时,请使用枚举器 (4.8, 14.4.6, 15.3)。例如:
class X { enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 }; // ... };
那样的话,其他地方就不需要成员定义了,你也不会想声明变量、浮点数等。
And in Appendix C (Technicalities) in Section C.5 (Constant Expressions), Stroustrup has this to say about "constant expressions":
在 C.5 节(常量表达式)的附录 C(技术)中,Stroustrup 对“常量表达式”有这样的说法:
In places such as array bounds (5.2), case labels (6.3.2), and initializers for enumerators (4.8), C++ requires a constant expression. A constant expression evaluates to an integral or enumeration constant. Such an expression is composed of literals (4.3.1, 4.4.1, 4.5.1), enumerators (4.8), and constsinitialized by constant expressions. In a template, an integer template parameter can also be used (C.13.3). Floating literals (4.5.1) can be used only if explicitly converted to an integral type. Functions, class objects, pointers, and references can be used as operands to the sizeofoperator (6.2) only.
Intuitively, constant expressions are simple expressions that can be evaluated by the compiler before the program is linked (9.1) and starts to run.
在数组边界 (5.2)、大小写标签 (6.3.2) 和枚举器的初始值设定项 (4.8) 等地方,C++ 需要 常量表达式。常量表达式的计算结果为整数或枚举常量。这样的表达是由文字(4.3.1,4.4.1,4.5.1),统计员(4.8),和consts由常量表达式初始化。在模板中,也可以使用整数模板参数(C.13.3)。浮动文字 (4.5.1) 仅在显式转换为整数类型时才能使用。函数、类对象、指针和引用只能用作sizeof运算符 (6.2) 的操作数。
直观地说,常量表达式是简单的表达式,可以在程序链接 (9.1) 并开始运行之前由编译器计算。
Note that he pretty much leaves out floating point as being able to play in 'constant expressions'. I suspect that floating point was left out of these types of constant expressions simply because they are not 'simple' enough.
请注意,他几乎忽略了浮点数,因为它可以在“常量表达式”中播放。我怀疑浮点数被排除在这些类型的常量表达式之外,仅仅是因为它们不够“简单”。
回答by Mark Ransom
I don't know why it would treat a double different from an int. I thought I had used that form before. Here's an alternate workaround:
我不知道为什么它会处理与 int 不同的 double。我以为我以前用过那个表格。这是一个替代的解决方法:
class Now_Better
{
static double const d;
};
And in your .cpp file:
在您的 .cpp 文件中:
double const Now_Better::d = 1.0;
回答by Liu Nick
here is my understanding based on Stroustrup's statement about in-class definition
这是我基于 Stroustrup 关于课堂定义的声明的理解
A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
类通常在头文件中声明,而头文件通常包含在许多翻译单元中。但是,为了避免复杂的链接器规则,C++ 要求每个对象都有唯一的定义。如果 C++ 允许需要作为对象存储在内存中的实体的类内定义,那么这条规则就会被打破。
http://www.stroustrup.com/bs_faq2.html#in-class
http://www.stroustrup.com/bs_faq2.html#in-class
so basically, this is not allowed because C++ do not allow this. In order to make linker rules more simple, C++ requires that every object has a unique definition.
所以基本上,这是不允许的,因为 C++ 不允许这样做。为了使链接器规则更简单,C++ 要求每个对象都有唯一的定义。
static member has only one instance in the class scope, not like regular static variables used heavily in C, which has only one instatnce inside one translation unit.
静态成员在类范围内只有一个实例,不像在 C 中大量使用的常规静态变量那样,它在一个翻译单元内只有一个实例。
If static member is defined in class, and the class definition will be included into many translation unit, so that the linker has to do more work to decide which static member should be used as the only one through all the related translation unit.
如果在类中定义了静态成员,则类定义将包含在许多翻译单元中,因此链接器必须做更多的工作来决定在所有相关翻译单元中应该使用哪个静态成员作为唯一一个。
But for regular static variables, they can only be used inside one translation unit, even in the case different static variables in different translation unit with same name, they will not affect each other. Linker can do simple work to link regular static variables inside one translation unit.
但是对于普通的静态变量,它们只能在一个翻译单元内使用,即使不同翻译单元中不同的静态变量同名,也不会相互影响。链接器可以做简单的工作来链接一个翻译单元内的常规静态变量。
in order to decrease the complications and give the base function, C++ provide the only in-class definition for a static const of integral or enumeration type.
为了减少复杂性并提供基函数,C++ 为整型或枚举类型的静态常量提供了唯一的类内定义。