【问题标题】:Do static inline variables need to be initialized before main?静态内联变量需要在main之前初始化吗?
【发布时间】:2017-11-30 08:59:40
【问题描述】:

如果我有以下A.h 文件(仅标题):

#pragma once

struct A{
  static inline struct Initializer{
     Initializer(){
        std::cout << "init A" << std::endl;
     }
  } initializer;
};

#include "A.h"(来自另一个标头,将包含在 main.cpp 中)是否足以让 Initializer::Initializer()main() 之前被调用?

我读到该标准要求仅在使用之前使用动态初始化来初始化静态变量。

是否动态初始化由实现定义 (8.5, 9.4, 12.1, 12.6.1) 完成命名空间范围的对象 在 main 的第一个语句之前。如果初始化被推迟 在 main 的第一个语句之后的某个时间点,它应该 在第一次使用中定义的任何函数或对象之前发生 与要初始化的对象相同的翻译单元。

#include 是否被视为“使用”?

【问题讨论】:

  • 局部静态变量(函数内部)在函数第一次被调用时被初始化。在调用main 之前初始化所有其他静态变量。
  • #include "file" 仅表示“在此处插入'文件'的内容”。它的行为与您使用复制和粘贴完全一样(但在报告诊断时调整行号)。
  • @Someprogrammerdude 我不认为这是真的……en.cppreference.com/w/cpp/language/initialization 请参阅“延迟动态初始化”

标签: c++ language-lawyer


【解决方案1】:

最新的工作草案在[basic.start.dynamic]/5中有更明确的措辞:

具有静态存储持续时间的非局部内联变量的动态初始化是在main的第一条语句之前排序还是延迟,由实现定义。如果它被延迟,它强烈地发生在该变量的任何非初始化 odr 使用之前。它是由实现定义的,在哪些线程和程序中的哪些点发生这种延迟的动态初始化。

地点:

非初始化 odr 使用是一种 odr 使用 ([basic.def.odr]) 不是由非本地静态或线程存储持续时间变量的初始化直接或间接引起的。


因此,回答您的问题:

#include "A.h"(来自另一个头文件,它将被 main.cpp 包含)是否足够,所以 Initializer::Initializer() 在 main() 之前被调用?

没有。 #include 还不够。你实际上必须odr-use它。

【讨论】:

    【解决方案2】:

    当我在您的问题的 cmets 中阅读由 tower120 提供的网站时,我发现了这一点:

    所有具有静态存储持续时间的非局部变量都作为程序启动的一部分在主函数开始执行之前进行初始化(除非延迟,见下文)。

    http://en.cppreference.com/w/cpp/language/initialization

    如果你的静态变量的初始化会被推迟,同一个网站说

    如果内联变量的初始化被延迟,它会在该特定变量的第一次 odr 使用之前发生。

    “odr-uses”是指访问变量或函数的地址(通过指针或引用)的情况。因此,如果您希望在 main() 之前调用 A::Initializer::Initializer(),则必须将其地址(引用指针)分配给非延迟静态变量,这将导致 A::Initializer 的第一次使用和 A::Initializer::Initializer() 的电话在main()之前发生

    #include 也不被视为“使用”,而是被视为“插入”。 Include 指令将文件的全部内容复制到它的位置。

    这是一个简单的例子:

    啊.h

    public class A {
        public:
            A() {};
        private:
            int var;
    }
    

    main.cpp

    #include "A.h"
    int main()
    {
        //Some Code
    }
    

    会变成这样

    public class A {
        public:
            A() {};
        private:
            int var;
    }
    int main()
    {
        //Some Code
    }
    

    这就是为什么你需要包含守卫或#pragma once 来防止两个文件包含相同的头代码来定义一个类或变量两次。

    【讨论】:

    • 如果 main.cpp 有#include A.h main() 的结尾并且根本没有使用struct A 怎么办。构造函数A::Initializer::Initializer() 还会被调用吗?
    • 代码中函数的顺序不会影响逻辑中调用的顺序,是吗?我引用的文本还提到所有非局部变量初始化都发生在“执行主函数之前”。并调用构造函数来初始化某些东西。所以是的,如果你把 #include "A.h" 放在 main() 后面,A::Initializer::Initializer() 仍然应该在 main() 之前调用。
    • 这是“动态初始化”的情况。因为“Initializer”不能静态构造(有副作用)。因此它可以被推迟。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-10
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    相关资源
    最近更新 更多