c – 由于定义顺序,“令人惊讶”的常量初始化

当阅读 slides about constexpr时,介绍是关于“惊奇的动态初始化与const”.例子是
struct S {
    static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;

唉,音轨丢失了,笔记也是这样,所以我只能猜测这里是什么意思.

它是否正确,d“动态”初始化,因为S :: c在d之前定义? S :: c的声明在d之前可能还不够,编译器需要完整的定义,对吗?

也就是说,我怀疑在下面的例子中,d会被静态初始化吗?

struct S {
    static const int c;
};
const int S::c = 5;
const int d = 10 * S::c;  // Now _after_ defn of S::c

而且,在C11中,要完全静态初始化是什么必须是constexpr? S :: c,d或两者?

解决方法

在第一个例子中,d不是由常量表达式初始化,因为S :: c不是

a non-volatile const object with a preceding initialization,
initialized with a constant expression

(参见C 11 [expr.const] p2,lvalue-to-rvalue转换的子弹),因为S :: c的初始化不在d的初始化之前.因此,静态初始化将用于S :: c(因为它是由常量表达式初始化的),但动态初始化可以用于d.

由于静态初始化先于动态初始化,d将通过动态初始化器初始化为50.允许编译器将d的动态初始化转换为静态初始化,但如果是,则必须产生如果每个可能使用动态初始化的变量实际上都使用动态初始化,那么d将会产生这个值.在这种情况下,d被初始化为50.有关详细信息,请参阅C 11 [basic.start.init] p2.

没有办法在第一个例子中添加constexpr来保证对d进行静态初始化;为了做到这一点,您必须重新排序初始化.但是,添加constexpr将会产生第一个示例的诊断,这将至少允许您确保不使用动态初始化(您将获得静态初始化或编译错误).

您可以更新第二种情况,以确保使用静态初始化如下:

struct S {
    static const int c; // do not use constexpr here
};
constexpr int S::c = 5;
constexpr int d = 10 * S::c;

在不是定义的变量声明上使用constexpr是不正确的,或者在不包含初始化器的变量声明上使用它,因此在struct S的定义中必须使用const,而不是constexpr.这个规则是一个例外,当定义一个文字非整数类型的静态constexpr数据成员时,该类在类中指定了初始化器:

struct T { int n; };
struct U {
    static constexpr T t = { 4 };
};
constexpr T U::t;

在这种情况下,必须在类的定义中使用constexpr,以便允许提供初始化器,并且必须在静态数据成员的定义中使用constexpr,以便允许其在常量表达式中使用.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...