13

在阅读有关 constexpr 的幻灯片时,介绍是关于“使用 consts 的令人惊讶的动态初始化”。例子是

struct S {
    static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;

唉,音轨不见了,笔记也不见了,所以我只能猜测这里的意思。

d动态初始化“令人惊讶”是否正确,因为之前S::c定义 dis before的声明可能还不够,编译器需要完整的定义对吧?S::cd

也就是说,我怀疑,在下面的例子中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 中,完全静态初始化需要什么?constexprS::cd或两者兼而有之?

4

4 回答 4

3

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

具有先前初始化的非易失性 const 对象,使用常量表达式初始化

(请参阅 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在不是定义的变量声明上使用它是不正确的,或者在不包含初始化程序的变量声明上使用它是不正确的,因此, constnotconstexpr必须在 的定义中使用struct S。此规则有一个例外,即在定义static constexpr文字、非整数类型的数据成员时,使用类中指定的初始化程序:

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

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

于 2011-12-30T20:37:32.670 回答
2

我相信 3.6.2 中确定何时发生静态初始化的规则不包括初始化 for d,因此是动态初始化。另一方面,S::c确实是静态初始化的(因为5是常量表达式)。由于所有静态初始化都发生在动态初始化之前,因此您会得到预期的结果。

要使d静态初始化符合条件,必须使用常量表达式对其进行初始化。这反过来又迫使您编写S::c内联:

struct S { static constexpr int c = 5; };

const int d = S::c; // statically initialized

请注意,该标准允许将动态初始化替换为静态初始化,这就是为什么重新排序原始示例中的两行将导致两种不同类型的初始化。正如 TonyK 指出的那样,您可以array[d]在静态情况下使用,但不能在动态情况下使用,因此您可以检查正在发生的情况。使用这种constexpr方法,您可以保证进行静态初始化,并且您不必依赖可选的编译器行为。

于 2011-10-02T11:54:54.347 回答
2

对于静态初始化,粗略地说,需要一个常量表达式初始化器。

要成为一个常量表达式,粗略地说,一个变量需要是一个const类型并且在前面有一个常量表达式的初始化。

在第一个 exampled的初始化程序不是常量表达式,S::c也不是(它没有前面的初始化)。因此,d不是静态初始化的。

在第二个 exampled的初始化程序是一个常量表达式,一切正常。

我在简化事情。在完整的正式标准中,这将是大约九倍。


至于说明符,不必声明constexpr任何对象constexpr这只是一个额外的错误检查。(这是关于constexpr 对象,而不是constexpr 函数)。

如果您想要一些额外的错误保护,您可以在第二个变体中声明(也许 5 明天将开始更改其值?)添加到第一个变体可能无济于事。S::c constexprconstexpr

于 2011-10-02T12:11:25.030 回答
1

您可以通过尝试声明数组来确定常量是静态初始化还是动态初始化:

struct S {
    static const int c;
};
const int d = 10 * S::c; // (1)
const int S::c = 5;      // (2)

static char array[d];

此代码在 g++ 版本 4.7.0 中失败,因为d是动态初始化的。如果交换 (1) 和 (2),它会编译,因为 nowd是静态初始化的。但我找不到另一种方法来修复它,使用constexpr.

于 2011-10-02T11:42:38.237 回答