当前位置: 首页 > 知识库问答 >
问题:

gcc中动态初始化先于静态初始化的原因

龚沛
2023-03-14
#include <iostream>
struct NonConstant{
    NonConstant(int v):v_(v){
        std::cout<<"NonConstant\n";
    }
    int v_;
};

struct Constant{
    constexpr Constant(int v):v_(v){
        if(v_==0){
         std::cout<<"Constant\n";
        }
    }
    int v_;
};

NonConstant a = 2; //#1
Constant b = 0;   //#2

int main(){
}

结果将是:

NonConstant
Constant

我对这个结果感到困惑,因为根据标准规则,#1不是静态初始化,#2是静态初始化,因为这些:

变量或临时对象o的常量初始值设定项是其完全表达式为常量表达式的初始值设定项,但如果o是对象,则这样的初始值设定项也可以为o及其子对象调用constexpr构造函数,即使这些对象是非文字类类型。
如果实体的常量初始值设定项初始化具有静态或线程存储持续时间的变量或临时对象,则执行常量初始化。如果不执行常量初始化,则将具有静态存储持续时间或线程存储持续时间的变量进行零初始化。零初始化和常量初始化合在一起称为静态初始化;所有其他初始化都是动态初始化。所有静态初始化都在([intro.races])任何动态初始化之前发生。

在这个问题的以下注释中,有人说除了构造函数的类可以是非文字类型之外,constexpr构造函数在任何方面都必须是有效的核心常量表达式,也就是说,std::cout的调用将使constexpr构造函数不是核心常量表达式。不过,我在cppreference中发现了另一种解读,那就是:

常数初始化是在静态和线程本地对象的零初始化之后(直到C++14)而不是(自C++14)执行的,并且是在所有其他初始化之前执行的。只有以下变量是常量初始化的:

  1. [...]
  2. 由构造函数调用初始化的类类型的静态或线程本地对象,如果构造函数是constexpr且所有构造函数参数(包括隐式转换)都是常量表达式,并且构造函数的初始值列表中的初始值设定项和类成员的大括号或相等初始值设定项仅包含常量表达式。

它并没有说constexpr构造函数必须是一个核心常量表达式。只要调用的构造函数满足它是由constexpr限定的,并且它的参数都必须是常量表达式,成员初始值设定项必须是常量表达式。因此,#2确实是一个常量初始化,因为参数0是一个常量表达式,并且由说明符constexpr限定的所选构造函数和成员初始化器遵守expr.const中提到的这些规则。

共有1个答案

向锦
2023-03-14

b具有动态初始化,而不是静态初始化。

正如您在[basic.start.statige]/2中的引号所解释的,b只有在其初始值设定项的完整表达式是常量表达式时才具有静态初始化,该表达式是constant(int)构造函数的执行。

在[expr.const]/2中,我们看到:

这里对构造函数“遵循抽象机器的规则”的评价包括构造函数体。并且由于初始值设定项是0,该计算将调用std::operator<<(std::ostream&,const char*),而不是constexpr。因此初始值设定项的完整表达式不是核心常量表达式,也不是常量表达式。

当然,尽管它不是严格的技术定义,“常量表达式”的全部意义是定义我们何时能保证编译器在编译时处理某些东西。而写入程序的标准输出肯定不会在编译时发生。

cpPreference.com是一个很好的资源,它试图做到尽可能准确,但它并不能取代实际标准的权威性。关于使用类构造函数进行常量初始化的引用对于C++14和C++17是不正确的。我怀疑它实际上是从C++11遗留下来的,在C++11中,constexpr构造函数的主体根本不允许计算任何函数调用,[expr.const]类似地描述了关于在核心常量表达式中使用constexpr构造函数的要求,即成员初始化器。

 类似资料:
  • 问题内容: 这是一段Java代码: 它如何编译?初始化后已执行变量“ ture”的声明。据我所知,静态块和字段已经按照它们出现的顺序执行了。 现在,为什么实例块中的值9已被打印3次?顺便说一句,该类的实例已创建了3次。那不是功课,我正在学习Java进行认证。 问题答案: 关于第一个问题,静态块确实按照它们出现的顺序进行处理,但是在处理静态块之前,先处理声明。声明作为类 准备工作 的一部分(JLS§

  • 我正在开发我的第一个 Swing 应用程序,现在提出了一个难题:在静态初始化期间或开始实际执行后执行引导和资源初始化。我是什么意思...我有单例: 因此,方法如下所示 或者,也许我在启动后手动初始化资源,然后运行它。逻辑上正确的方式是什么?

  • 问题内容: 我想知道为什么默认情况下C,C ++和Java中的确切静态变量初始化为零?为什么对局部变量不是这样? 问题答案: 为什么要对静态变量进行确定性初始化而对局部变量不进行初始化? 了解如何实现静态变量。 它们的内存在链接时分配,并且它们的初始值也在链接时提供。 没有运行时开销。 另一方面,用于局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前在那里。如果需要,可以清除该内存(将其清

  • 问题内容: 据我了解,“静态初始化块”用于设置静态字段的值(如果无法在一行中完成的话)。 但是我不明白为什么我们需要为此设置一个特殊的块。例如,我们将字段声明为静态(无值分配)。然后编写几行代码,生成并为上面声明的静态字段分配一个值。 为什么我们需要在这样一个特殊的块这样的行:? 问题答案: 在非静态块: 每次构造类的实例时被调用。在静态块只被调用一次,当类本身初始化,无论该类型的有多少对象创建。

  • 问题内容: 当我试图写这样的东西: 编译器无法编译它。 但是当我写这样的东西时: 并反编译后,我看到了静态初始化: 您能否向我解释这种行为? 问题答案: 您可以具有静态初始化,但不能具有静态块。静态初始化需要静态代码块来实现的事实确实改变了Java语法。 关键是您不打算在接口中包含代码(在Java 8之前),但是可以初始化字段。 顺便说一句,您可以拥有一个嵌套的类或枚举,该类或枚举具有您想要的尽可

  • 问题内容: Kotlin 中的静态初始化块等效于什么? 我了解Kotlin的设计宗旨是不带静电。我正在寻找具有等效语义的东西-首次加载该类时,代码将运行一次。 我的特定用例是,我想从Android AppCompat库启用DayNight功能,并且说明要求将一些代码放入类的静态初始化块中。 问题答案: 从某种角度看,Kotlin中的s等同于Java类的静态部分。特别是,它们是在首次使用类之前初始化