C++ 为什么我可以初始化静态常量字符,但不能初始化类定义中的静态常量双精度

C++ Why can I initialize a static const char but not a static const double in a class definition?

本文关键字:常量 静态 初始化 定义 双精度 字符 为什么 我可以 C++ 但不能      更新时间:2023-10-16

这里有两行代码:

static const double RYDBERG_CONST_EV = 13.6056953;
static const char CHAR_H_EDGE = '-';

第二行编译没有错误,第一行不编译。(错误: 'constexpr' needed for in-class initialization of static data member... (

解决方案显然是在类型之前添加关键字constexpr。这是必需的,因为double不是"整体类型"。但是,为什么整数和浮点类型之间的行为不同?

我不认为这有一个很好的理由,除了它在历史上已经增长。

整型的例外在 C++11 之前是可取的,因为人们希望将它们用作数组大小。这与将积分const蚂蚁视为常量表达式的另一个例外情况一致。浮点类型不存在的异常。

const int    ni = 10;
const float  nf = 10.0f;
int numbers1[(unsigned) ni];  // perfectly fine in all versions of C++
int numbers2[(unsigned) nf];  // error in all versions of C++

当C++11引入constexpr时,它可以做任何const整体类型的特殊外壳可以做的事情,甚至更多。它对任何文字类型的工作方式都相同。因此,给定一个高级工具,没有必要将整数类型的现有规则扩展到浮点数。

今天,整体类型的特殊外壳大多是早期黑暗时期的遗留物。它不能从语言中删除,因为这样做会破坏依赖于这种特殊大小写的现有代码,但是通过添加更多由于constexpr而今天完全不需要的例外使语言进一步复杂化几乎没有什么好处。应该期望人们迁移到constexpr,而不再担心旧的拐杖。我相信这是一个非常合理的决定,但你当然可以争辩说应该做出另一个决定。

补遗

正如T.C.所评论的那样,关于这个问题有一份(非(缺陷报告,委员会确认行为不会改变,人们应该开始使用constexpr

1826. 常量表达式中的浮点const

部分: 5.20 [expr.const] 状态: NAD 提交者: 维尔·沃蒂莱宁 日期: 2014-01-04

使用常量初始化的const整数可以在常量表达式中使用,但不能使用常量初始化的const浮点变量。这是有意为之,与 C++03 兼容,同时鼓励一致使用 constexpr .然而,有些人发现这种区别令人惊讶。

还观察到,允许const浮点变量作为常量表达式将是一个 ABI 中断性更改,因为它会影响 lambda 捕获。

一种可能性是弃用在常量表达式中使用const积分变量。

补充说明,2015 年 4 月:

EWG要求CWG允许在常量表达式中使用const浮点变量。

理由(2015年5月(:

CWG认为当前的规则不应该改变,希望浮点值参与常量表达式的程序员应该使用constexpr而不是const

对于措辞,§ [class.static.data]/3 说:

  1. 如果非易失性 const 静态数据成员是整型或枚举类型,则其在类中的声明 定义可以指定一个大括号或等于初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是一个常量表达式 (5.20(。文本类型的静态数据成员可以使用 constexpr 说明符在类定义中声明;如果是这样,则其声明应指定一个大括号或等于初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式。

(我的(。请注意,在整型的情况下,静态数据成员可以(不一定(具有初始化(您始终可以将其放在类定义之外(。另外,在类定义中进行初始化的唯一其他方法是通过constexpr。

关于允许在类定义中初始化整型类型(也在 C++98 中(的原因(恕我直言(是为了启用非常简单的模式,如下所示:

class Foo {
    static const size_t arrayLen = 42;
    int foo[arrayLen];
};

如果没有体内初始化,将无法实现。

众所周知:

  • static const整型成员都可以在类定义中初始化。

  • 可以在类定义中初始化static constexpr成员

double不是整型类型,应标记为constexpr

在计算机中生成的可执行文件可以在浮点表示计算不同的其他计算机中运行。整型常量表达式不会改变。

标记一个对象static表示所有观察者都可以知道它,而将其标记为const表示值不会改变。编译器可以生成一个值(例如 314 (并将其放在只读部分,因为范围是在标准中定义的。

另一方面,double不在标准中,不能将其范围检查和编译时的值存储在类定义中。人们很容易最终拥有不同的对象文件,而对象对该static const double具有不同的值,从而破坏了ODR。

下面是一个简单的示例:

struct foo
{
static char const a = 1/11; // everyone gets 0 at compile-time
};

你会说,但这可能发生在双打身上,乍一看,像这样

struct foo
{
static double const y=1.0/11.0; // the true value is 0.090909... 
};

似乎是可以验证的,但是一台机器中的双精度表示将在另一台机器中0.09091 0.090909091.

使用constexpr允许向编译器说,验证这一点所需的输入在编译时可用。但是,实际评估可以在运行时进行。

由于C++编译器生成的目标文件可以移动到具有不同浮点表示的机器上,因此您必须告诉必须在编译时进行此检查以确保一致性。

这个问题是XY问题的典型例子。而不是问,">为什么我必须用constexpr标记任何东西?"给出了一个char -vs- float的谜题。现在的问题是,">为什么我们必须对非整型使用constexpr?",在这里你可以找到你的答案。