为什么我不能在类中有一个非整型静态const成员?
我注意到C ++不会编译以下内容:
class No_Good { static double const d = 1.0; };
然而,它会愉快地允许一个变化,其中双变化为一个int,无符号,或任何整型:
class Happy_Times { static unsigned const u = 1; };
我的解决办法是把它改为:
class Now_Good { static double d() { return 1.0; } };
并且认为编译器将足够聪明以便在必要时进行内联…但是这让我感到好奇。
为什么C + +devise器允许我静态const int或unsigned,但不是double?
编辑:我在Windows XP上使用visual studio 7.1(.net 2003)。
EDIT2:
问题已经回答,但为了完成,我看到的错误:
error C2864: 'd' : only const static integral data members can be initialized inside a class or struct
问题是,对于一个整数,编译器通常不必为常量创build一个内存地址。 它在运行时不存在,每一次使用它都会被内联到周围的代码中。 它仍然可以决定给它一个内存位置 – 如果它的地址已经被使用了(或者如果它被const引用传递给一个函数),它必须。 为了给它一个地址,它需要在一些翻译单元中定义。 在这种情况下,您需要将声明与定义分开,否则将以多个翻译单位定义。
使用g ++没有优化( -O0
),它会自动内联常量整数variables,而不是常量double值。 在更高的优化级别(例如-O1
)中,它将内置常量。 因此,下面的代码在-O1
编译而不是在-O0
:
// File ah class X { public: static const double d = 1.0; }; void foo(void); // File a.cc #include <stdio.h> #include "ah" int main(void) { foo(); printf("%g\n", X::d); return 0; } // File b.cc #include <stdio.h> #include "ah" void foo(void) { printf("foo: %g\n", X::d); }
命令行:
g++ a.cc b.cc -O0 -oa # Linker error: ld: undefined symbols: X::d g++ a.cc b.cc -O1 -oa # Succeeds
为了实现最大的可移植性,您应该在头文件中声明常量,并在源文件中定义它们一次。 没有优化,这不会损害性能,因为你不是优化反正,但启用优化,这可能会伤害性能,因为编译器不能再将这些常量内联到其他源文件,除非启用“整个程序优化” 。
我没有看到技术上的原因
struct type { static const double value = 3.14; };
是禁止的。 任何你find它的地方都是由于非可移植实现定义的特性。 他们似乎也只是有限的使用。 对于在类定义中初始化的整型常量,可以使用它们并将它们作为非typesparameter passing给模板,并将它们用作数组维度的大小。 但是你不能这样做浮点常量。 允许浮点模板参数将带来自己的一套规则不是真正值得的麻烦。
尽pipe如此,下一个C ++版本将允许使用constexpr
:
struct type { static constexpr double value = 3.14; static constexpr double value_as_function() { return 3.14; } };
并将使type::value
成为一个常量expression式。 同时,你最好的select是遵循std::numeric_limits
也使用的模式:
struct type { static double value() { return 3.14; } };
它不会返回一个常量expression式(在编译时不知道这个值),但是这只是理论上的问题,因为实际的值将被内联。 请参阅constexpr提议。 它包含
4.4
Floating-point constant expressions
传统上,在编译时对浮点常量expression式的评估是一个棘手的问题。 为了一致性和一般性,我们build议允许浮点types的常量expression式数据,用任何浮点常量expression式进行初始化。 这也将增加与C99 [ISO99,第6.6节]的兼容性
[#5]在几个上下文中需要一个expression式,其值为一个常量。 如果在翻译环境中评估浮动expression式,则算术精度和范围应至less与在执行环境中评估expression式一样大。
它并没有给出一个理由,但是这里是Stroustrup在“C ++编程语言第三版”中对此的解释:
10.4.6.2成员常数
也可以通过在其成员声明中添加一个常量expression式初始值设定项来初始化一个静态整型常量成员。 例如:
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 }; // ... };
这样,其他地方就不需要成员定义了,而且你也不想试图声明variables,浮点数等等。
在C.5节(常量expression式)的附录C(技术)中,Stroustrup对“常量expression式”有这样的说法:
在诸如数组边界(5.2),情况标签(6.3.2)和枚举器初始化器(4.8)等地方,C ++需要一个常量expression式 。 常量expression式评估为整数或枚举常量。 这样的expression式由文字(4.3.1,4.4.1,4.5.1),枚举符 (4.8)和由常量expression式初始化的常量组成。 在模板中,也可以使用整数模板参数(C.13.3)。 浮点文字(4.5.1)只能在明确转换为整型时使用。 函数,类对象,指针和引用只能用作sizeof运算符(6.2)的操作数。
直观上,常量expression式是简单的expression式,可以在程序链接(9.1)并开始运行之前由编译器进行评估。
请注意,他几乎没有考虑到可以在“常量expression式”中播放浮点。 我怀疑这些types的常量expression式中没有提供浮点数,仅仅是因为它们不够“简单”。
我不知道为什么它会对待一个不同于int的double。 我以为我曾经使用过这种forms。 这是另一种解决方法:
class Now_Better { static double const d; };
并在您的.cpp文件中:
double const Now_Better::d = 1.0;
这里是基于Stroustrup关于课堂定义的陈述的理解
一个类通常在头文件中声明,而头文件通常包含在许多翻译单元中。 但是,为了避免复杂的链接器规则,C ++要求每个对象都有唯一的定义。 如果C ++允许将需要作为对象存储在内存中的实体的类定义中断,那么该规则将被破坏。
http://www.stroustrup.com/bs_faq2.html#in-class
所以基本上这是不允许的,因为C ++不允许这样做。 为了使链接器规则更简单,C ++要求每个对象都有唯一的定义。
静态成员在类范围内只有一个实例,不像C中常用的常量静态variables,在一个翻译单元内只有一个instatnce。
如果在类中定义了静态成员,并且类定义将被包含到许多翻译单元中,那么链接器必须通过所有相关的翻译单元做更多的工作来确定哪个静态成员应该被用作唯一的静态成员。
但是对于普通的静态variables,只能在一个翻译单元内使用,即使在不同翻译单位中同名的不同静态variables的情况下,也不会相互影响。 链接器可以做简单的工作来链接一个翻译单元内的常规静态variables。
为了减less复杂性并给出基函数,C ++为整数或枚举types的静态常量提供了唯一的类内定义。