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

发布于 2024-12-07 12:14:28 字数 878 浏览 0 评论 0原文

当阅读关于 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::cd 或两者?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

习惯成性 2024-12-14 12:14:28

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

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

在本例中,constexpr< /code> 必须在类的定义中使用,以便允许提供初始值设定项,并且 constexpr 必须在静态数据成员的定义中使用,以便允许使用它在常量表达式中。

In the first example, d is not initialized by a constant expression, because S::c is not

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

(see C++11 [expr.const]p2, bullet on lvalue-to-rvalue conversions), because the initialization of S::c does not precede the initialization of d. Therefore static initialization will be used for S::c (because it is initialized by a constant expression), but dynamic initialization can be used for d.

Since static initialization precedes dynamic initialization, d would be initialized to 50 by its dynamic initializer. The compiler is permitted to convert the dynamic initialization of d to static initialization, but if it does, it must produce the value that d would have had if every variable which could have used dynamic initialization had, in fact, used dynamic initialization. In this case, d is initialized to 50 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 for d; in order to do that, you must reorder the initializations. However, adding constexpr 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:

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

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, so const, not constexpr must be used within the definition of struct S. There is one exception to this rule, which is when defining a static constexpr data member of a literal, non-integral type, with the initializer specified within the class:

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

In this case, constexpr must be used in the definition of the class, in order to permit an initializer to be provided, and constexpr must be used in the definition of the static data member, in order to allow its use within constant expressions.

只怪假的太真实 2024-12-14 12:14:28

我相信 3.6.2 中列出的用于确定何时发生静态初始化的规则不包括 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 方法,您可以保证进行静态初始化,并且不必依赖可选的编译器行为。

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 (since 5 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 the S::c inline:

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

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

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 the constexpr approach, you're guaranteed to have static initialization and you don't have to rely on optional compiler behaviour.

原谅我要高飞 2024-12-14 12:14:28

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

粗略地说,要成为常量表达式,变量必须是 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, as S::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 declared constexpr. It is just an additional error-check. (This is about constexpr objects, not constexpr 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?) Adding constexpr to the first variant cannot possibly help.

禾厶谷欠 2024-12-14 12:14:28

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

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),它就会编译,因为现在 d 已静态初始化。但我找不到另一种方法来修复它,使用 constexpr

You can find out whether a constant is statically or dynamically initialised by trying to declare an array:

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

static char array[d];

This code fails in g++ version 4.7.0, because d is dynamically initialised. And if you exchange (1) and (2), it compiles, because now d is statically initialised. But I can't find another way to fix it, using constexpr.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文