静态常量 double 不能有类内初始值设定。为什么?

48

以下代码的问题在于,类型为“const double”的静态成员不能具有类内初始值设定项。为什么在以下代码中只适用于“const double”?请帮忙。

class sample{
   static const char mc = '?';
   static const double md = 2.2;
   static const bool mb = true;
};
const char sample::mc;
const double sample::md;
const bool sample::mb;
int main(){
}
3个回答

67

C++03语言标准实现的逻辑是基于以下原则。

在C++中,初始化程序是对象定义的一部分。对于静态成员,在类内部编写的部分实际上只是一个声明。因此,严格来说,在类内直接为任何静态成员指定初始值是“不正确”的。这与该语言的一般声明/定义概念相反。无论你在类内部声明了哪些静态数据,都必须在稍后进行定义。那时你才有机会指定初始化程序。

对于静态整数常量,这个规则有一个例外,因为在C ++中,这样的常量可以形成整数常量表达式(ICE)。 ICE在语言中起着重要作用,为了使它们按预期工作,整数常量的值必须在所有翻译单元中可见。为了使某个常量的值在所有翻译单元中可见,它必须在声明点上可见。为了实现这一点,语言允许在类内直接指定初始化程序。

此外,在许多硬件平台上,常量整数操作数可以直接嵌入到机器指令中。或者常量可以完全消除或替换(例如,乘以8可以实现为移位3)。为了方便生成具有嵌入操作数和/或各种算术优化的机器代码,重要的是将整数常量的值在所有翻译单元中可见。

非整数类型没有任何类似ICE的功能。此外,硬件平台通常不允许直接将非整数操作数嵌入到机器指令中。因此,上述“规则例外”不适用于非整数类型。它简单地没有作用。


5
为什么对于双精度数,没有像规则一样做出特殊的例外呢?将浮点常量嵌入机器指令中是有意义的吗? - anton_rh
5
@anton_rh: 好问题。就纯概念而言,我不知道为什么它被禁止了。至于实际考虑因素,大多数平台根本无法嵌入浮点常量(现在仍然如此)。其中一个原因可能是浮点常量对于这种嵌入来说太大了。然而,在C++11中,constexpr可以让你规避这个限制,至少在概念上是这样的。 - AnT stands with Russia

33
编译器建议我使用constexpr代替const
static_consts.cpp:3:29: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double sample::md’ of non-integral type [-fpermissive]
static_consts.cpp:7:22: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double sample::md’ of non-integral type [-fpermissive]

我刚刚接受了这个offer:

class sample{
   static const char mc = '?';
   static constexpr double md = 2.2;
   static const bool mb = true;
};
const char sample::mc;
const bool sample::mb;
int main(){
}

现在它可以正常编译(C++11)。


14
你的编译器比我的编译器(MSVC++)更有用! - Matthias
非常好的答案。正是我所需要的。谢谢。 - Vasantha Ganesh
这并没有解释为什么 static const 必须是整数类型。只是新版本的 C++ 允许使用 static constexpr 双精度浮点数。 - user904963

15

在 C++11 之前,只有const 整数类型可以在类定义中直接初始化。这只是标准强加的限制。

使用 C++11,这种限制已不再适用。


实际上,根据[class.static.data]p3,只有当静态数据是非易失性的整数或枚举类型的const时,才可以在类定义中初始化。 - Angew is no longer proud of SO
@Angew 您是在谈论“其在类定义中的声明可以指定一个花括号或等号初始化器,其中每个初始化器子句都是赋值表达式且均为常量表达式”吗?因为那是不同的 - 它是关于花括号初始化的。 - Luchian Grigore
这有什么不同吗?根据[dcl.init],brace-or-equal-initializer ::= = initializer-clause | braced-init-list - Angew is no longer proud of SO
1
仍然无法使用C++11编译器甚至C++14编译器编译这段代码。 - anton_rh
@anton_rh 你使用哪个编译器? - Luchian Grigore
4
只要我们在特别提到static const时,C++11中就没有任何更改。关于静态成员的规则在这方面基本上保持不变。即使在C++14中,static const double仍然不能在类内初始化。然而,constexpr有所不同 - 它支持立即初始化程序。 - AnT stands with Russia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接