c++ - "surprising"常量初始化因为定义顺序

标签 c++ initialization constants c++11 constexpr

阅读 slides about constexpr 时介绍是关于“令人惊讶的 consts 动态初始化”。例子是

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

为了获得蛋糕,在 C++11 中,必须是 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,关于左值到右值转换的项目符号),因为 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 或在不包含初始化程序的变量声明上使用它是不正确的,所以 const code>,而不是 constexpr 必须在 struct S 的定义中使用。此规则有一个异常(exception),即在定义文字、非整数类型的 static constexpr 数据成员时,使用类中指定的初始化程序:

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

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

关于c++ - "surprising"常量初始化因为定义顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7625952/

相关文章:

c++ - 打开文件后程序崩溃

c++ - 如何通过数据包解析在 FTP 上传/下载中获取源和目标的完整路径?

c++ - 使用统一初始化初始化对象和指针对象不起作用

java - 我什么时候应该在构造函数内部初始化类字段,什么时候应该在构造函数外部初始化?

java - 二维 ArrayList 初始化行

c - 初始化 static char const *somevar

c++ - 将任何函数作为模板参数传递

c++ - iOS 上的可访问目录

Python:我应该使用常量还是属性?

c++ - 编译错误是因为缺少const吗?