“令人惊讶的”由于定义顺序而常量初始化
当阅读关于 constexpr 的幻灯片时,介绍是关于的”令人惊讶的是使用 const 进行动态初始化”。这个例子很
struct S {
static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;
可惜,音轨丢失了,音符也丢失了,所以我只能猜测这里的意思。
d
“令人惊讶地”动态初始化是否正确,因为 S::c
是在 d之前定义的code>?
S::c
的声明位于 d
之前可能还不够,编译器需要完整的 < em>定义,对吗?
也就是说,我怀疑在下面的示例中 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
或两者?
When reading the slides about constexpr the introduction is about "surprisingly dynamic initialization with consts". The example is
struct S {
static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;
Alas, the audio track is missing, so are the notes, so I can only guess what is meant here.
Is it corrrect that d
is "surprisingly" initialized dynamically, because S::c
is defined before d
? That the declaration of S::c
is before d
is probably not enough, the compiler needs the complete definition, right?
That said, I suspect, that in the following example d
would be initialized statically?
struct S {
static const int c;
};
const int S::c = 5;
const int d = 10 * S::c; // now _after_ defn of S::c
And to take the cake, in C++11, what would have to be constexpr
for full static initialization? S::c
, d
or both?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在第一个示例中,
d
不是由常量表达式初始化的,因为S::c
不是由常量表达式初始化的。(请参阅 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 将为第一个示例生成诊断,这至少可以让您确保不使用动态初始化(您会得到静态初始化或编译错误)。
您可以更新第二种情况以确保使用静态初始化,如下所示:
在不是定义的变量声明上使用
constexpr
是不正确的格式,或者在不是定义的变量声明上使用它是不正确的。不包含初始化程序,因此在 struct S 的定义中必须使用const
,而不是constexpr
。此规则有一个例外,即定义文字非整数类型的static constexpr
数据成员,并在类中指定初始值设定项时:在本例中,
constexpr< /code> 必须在类的定义中使用,以便允许提供初始值设定项,并且
constexpr
必须在静态数据成员的定义中使用,以便允许使用它在常量表达式中。In the first example,
d
is not initialized by a constant expression, becauseS::c
is not(see C++11 [expr.const]p2, bullet on lvalue-to-rvalue conversions), because the initialization of
S::c
does not precede the initialization ofd
. Therefore static initialization will be used forS::c
(because it is initialized by a constant expression), but dynamic initialization can be used ford
.Since static initialization precedes dynamic initialization,
d
would be initialized to50
by its dynamic initializer. The compiler is permitted to convert the dynamic initialization ofd
to static initialization, but if it does, it must produce the value thatd
would have had if every variable which could have used dynamic initialization had, in fact, used dynamic initialization. In this case,d
is initialized to50
either way. See C++11 [basic.start.init]p2 for more information on this.There is no way to add
constexpr
to the first example to guarantee that static initialization is used ford
; in order to do that, you must reorder the initializations. However, addingconstexpr
will produce a diagnostic for the first example, which will at least allow you to ensure that dynamic initialization is not used (you get static initialization or a compilation error).You can update the second case to ensure that static initialization is used as follows:
It is ill-formed to use
constexpr
on a variable declaration which is not a definition, or to use it on a variable declaration which does not contain an initializer, soconst
, notconstexpr
must be used within the definition ofstruct S
. There is one exception to this rule, which is when defining astatic constexpr
data member of a literal, non-integral type, with the initializer specified within the class:In this case,
constexpr
must be used in the definition of the class, in order to permit an initializer to be provided, andconstexpr
must be used in the definition of the static data member, in order to allow its use within constant expressions.我相信 3.6.2 中列出的用于确定何时发生静态初始化的规则不包括
d
的初始化,因此是动态初始化 >。另一方面,S::c
确实是静态初始化的(因为5
是一个常量表达式)。由于所有静态初始化都发生在动态初始化之前,因此您会得到预期的结果。要使 d 适合静态初始化,必须使用常量表达式对其进行初始化。这反过来又迫使您内联编写
S::c
:请注意,标准允许用静态初始化替换动态初始化,这就是为什么对原始示例中的两行重新排序将导致两个不同种类的初始化。正如 TonyK 指出的,您可以在静态情况下使用 array[d] ,但不能在动态情况下使用,因此您可以检查发生的是哪一种情况。使用
constexpr
方法,您可以保证进行静态初始化,并且不必依赖可选的编译器行为。I believe that the rules laid out in 3.6.2 to determine when static initialization happens do not include the initialization for
d
, which is therefore dynamic initialization. On the other hand,S::c
is indeed statically initialized (since5
is a constant expression). Since all static initialization happens before dynamic initialization, you get the expected result.To make
d
eligible for static initialization, it has to be initialized with a constant expression. This in turn forces you to write theS::c
inline:Note that the standard permits dynamic initialization to be replaced by static initialization, which is why reordering the two lines in your original example will cause the two different sorts of initialization. As TonyK points out, you can use
array[d]
in the static case, but not in the dynamic case, so you can check which one is happening. With theconstexpr
approach, you're guaranteed to have static initialization and you don't have to rely on optional compiler behaviour.粗略地说,对于静态初始化,需要一个常量表达式初始化程序。
粗略地说,要成为常量表达式,变量必须是 const 类型,并且使用常量表达式进行预先初始化。
在第一个示例中,
d
的初始值设定项不是常量表达式,因为S::c
不是常量表达式(它没有前面的初始化)。因此,d
不是静态初始化的。在第二个示例中,
d
的初始值设定项是一个常量表达式,一切正常。我正在简化事情。在完全正式的标准语言中,这大约要长九倍。
对于
constexpr
说明符,不必声明任何对象constexpr
。这只是一个额外的错误检查。 (这是关于constexpr
对象,而不是constexpr
函数)。如果您想要一些额外的错误保护,您可以在第二个变体中声明
S::c
constexpr
(也许 5 明天就会开始更改其值? )将constexpr
添加到第一个变体不可能有帮助。For static initialization one needs, roughly speaking, a constant-expression initializer.
To be a constant-expression, roughly speaking, a variable needs to be of a
const
type and have a preceding initialization with a constant-expression.In the first example
d
's initializer is not a constant-expression, asS::c
isn't one (it has no preceding initialization). Hence,d
is not statically initialized.In the second example
d
's initializer is a constant-expression, and everything is OK.I'm simplifying matters. In full formal standardese this would be about nine times longer.
As for
constexpr
specifier, no object has to be declaredconstexpr
. It is just an additional error-check. (This is aboutconstexpr
objects, notconstexpr
functions).You may declare
S::c
constexpr
in the second variant if you want some extra error protection (perhaps 5 will start changing its value tomorrow?) Addingconstexpr
to the first variant cannot possibly help.您可以通过尝试声明数组来确定常量是静态初始化还是动态初始化:
此代码在 g++ 版本 4.7.0 中失败,因为
d
是动态初始化的。如果交换 (1) 和 (2),它就会编译,因为现在 d 已静态初始化。但我找不到另一种方法来修复它,使用constexpr
。You can find out whether a constant is statically or dynamically initialised by trying to declare an array:
This code fails in g++ version 4.7.0, because
d
is dynamically initialised. And if you exchange (1) and (2), it compiles, because nowd
is statically initialised. But I can't find another way to fix it, usingconstexpr
.