我注意到C++不会编译以下内容:
class No_Good { static double const d = 1.0; };
但是,它会愉快地允许将double更改为int,unsigned或任何整数类型的变体:
class Happy_Times { static unsigned const u = 1; };
我的解决方案是将其改为:
class Now_Good { static double d() { return 1.0; } };
并认为编译器将足够智能以在必要时内联...但它让我很好奇.
为什么C++设计器允许我使用静态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
Adam Rosenfi.. 48
问题是,对于整数,编译器通常不必为常量创建内存地址.它在运行时不存在,并且每次使用它都会内联到周围的代码中.它仍然可以决定给它一个内存位置 - 如果它的地址被采用(或者它是由const引用传递给函数),它必须.为了给它一个地址,需要在一些翻译单元中定义.在这种情况下,您需要将声明与定义分开,否则它将在多个翻译单元中定义.
使用没有optimize(-O0
)的g ++ ,它会自动内联常量整数变量,但不会是常量double值.在更高的优化级别(例如-O1
),它内联不变的双精度.因此,以下代码编译-O1
但不是-O0
:
// File a.h class X { public: static const double d = 1.0; }; void foo(void); // File a.cc #include#include "a.h" int main(void) { foo(); printf("%g\n", X::d); return 0; } // File b.cc #include #include "a.h" void foo(void) { printf("foo: %g\n", X::d); }
命令行:
g++ a.cc b.cc -O0 -o a # Linker error: ld: undefined symbols: X::d g++ a.cc b.cc -O1 -o a # Succeeds
为了获得最大的可移植性,您应该在头文件中声明常量,并在某个源文件中定义它们一次.没有优化,这不会损害性能,因为你没有进行优化,但是启用了优化,这会损害性能,因为编译器不能再将这些常量内联到其他源文件中,除非你启用"整个程序优化" .
问题是,对于整数,编译器通常不必为常量创建内存地址.它在运行时不存在,并且每次使用它都会内联到周围的代码中.它仍然可以决定给它一个内存位置 - 如果它的地址被采用(或者它是由const引用传递给函数),它必须.为了给它一个地址,需要在一些翻译单元中定义.在这种情况下,您需要将声明与定义分开,否则它将在多个翻译单元中定义.
使用没有optimize(-O0
)的g ++ ,它会自动内联常量整数变量,但不会是常量double值.在更高的优化级别(例如-O1
),它内联不变的双精度.因此,以下代码编译-O1
但不是-O0
:
// File a.h class X { public: static const double d = 1.0; }; void foo(void); // File a.cc #include#include "a.h" int main(void) { foo(); printf("%g\n", X::d); return 0; } // File b.cc #include #include "a.h" void foo(void) { printf("foo: %g\n", X::d); }
命令行:
g++ a.cc b.cc -O0 -o a # Linker error: ld: undefined symbols: X::d g++ a.cc b.cc -O1 -o a # Succeeds
为了获得最大的可移植性,您应该在头文件中声明常量,并在某个源文件中定义它们一次.没有优化,这不会损害性能,因为你没有进行优化,但是启用了优化,这会损害性能,因为编译器不能再将这些常量内联到其他源文件中,除非你启用"整个程序优化" .
我认为没有技术原因
struct type { static const double value = 3.14; };
禁止.在任何可以找到其工作原理的场合都是由于非便携式实现定义的功能.它们似乎也只是有限的用途.对于在类定义中初始化的整数常量,您可以使用它们并将它们作为非类型参数传递给模板,并将它们用作数组维度的大小.但是你不能这样做浮点常数.允许浮点模板参数会带来自己的一套规则并不值得麻烦.
尽管如此,下一个C++版本将允许使用constexpr
:
struct type { static constexpr double value = 3.14; static constexpr double value_as_function() { return 3.14; } };
并会type::value
不断表达.与此同时,您最好的选择是遵循以下模式std::numeric_limits
:
struct type { static double value() { return 3.14; } };
它不会返回一个常量表达式(在编译时不知道值),但这只是理论上的问题,因为实际上该值无论如何都会被内联.请参阅constexpr提案.它包含
4.4
Floating-point constant expressions
传统上,在编译时评估浮点常数表达式是一个棘手的问题.为了统一性和通用性,我们建议允许浮点数类型的常量表达数据,用任何浮点常数表达式初始化.这也将增加与允许的C99 [ISO99,§6.6]的兼容性
[#5]在几个上下文中需要一个求值为常量的表达式.如果在翻译环境中评估浮动表达式,则算术精度和范围应至少与在执行环境中评估表达式一样大.
它并没有真正给出理由,但这就是Stroustrup在"The C++ Programming Language Third Edition"中对此的看法:
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 }; // ... };这样,其他地方不需要成员定义,并且您不会想要声明变量,浮点数等.
在C.5节(常量表达式)的附录C(技术性)中,Stroustrup对此有关"常量表达式"的说法:
在诸如数组边界(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)并开始运行之前由编译器进行评估.
请注意,他几乎没有留下浮点作为能够在"常量表达式"中播放.我怀疑浮点数被排除在这些类型的常量表达式之外,仅仅因为它们不够"简单".