什么是无序动态初始化,部分有序动态初始化和有序动态初始化

时间:2020-09-18 07:55:58

标签: c++

我正在通过Dynamic Initialization进行阅读,但由于缺少什么是无序动态初始化,部分有序动态初始化和有序动态初始化的例子,所以无法完全理解。

任何人都可以提供相同的内容-这将使理论更加清晰?

1 个答案:

答案 0 :(得分:2)

动态初始化

动态初始化涉及未使用constexpr表达式初始化的非局部变量。

int foo();
int global = foo();

正如cppreference所详细解释的那样,编译器有一定的余地,可以安排使用静态代码进行动态初始化,也可以推迟它,只要它不会改变程序的行为即可。

请使用=foo()将所有初始化视为动态的,并具有足够的副作用以创建用于无序初始化的UB。例如:

int foo(){//Pretend to be flexible on the return type
   static int i =0;
   return ++i;
}

以下行为由[basic.start.dynamic]控制,从现在开始称为规则

  • 具有静态存储持续时间的非局部变量的动态初始化是无序的 如果变量是隐式或显式实例化的 专门化,如果变量是内联,则部分排序 不是隐式或显式实例化的变量 专业化,否则必须订购。

    [注意: 专门的非内联静态数据成员或变量模板 专业化已命令初始化。 —尾注]

  • 在以下情况下,声明D在声明E之前出现:

    • D与E出现在相同的翻译单位中,或者
    • 翻译 包含E的单元对翻译单元具有接口依赖性 包含D,无论哪种情况都在E之前。
  • 具有静态存储持续时间的非局部变量V和W的动态初始化的顺序如下:

    • 如果V和W已订购 初始化,并且V的定义在出现之前按顺序排列 W的定义,或者如果V具有部分有序的初始化,则W 没有无序的初始化,并且对于的每个定义E W存在V的定义D,使得D按外观排列 在E之前,然后
      • 如果程序未启动线程 ([intro.multithread])而不是主线程([basic.start.main]) 或V和W已排序初始化,它们在 同一翻译单元,V的初始化在 W的初始化;
      • 否则,V的初始化 强烈发生在W初始化之前。
    • 否则,如果 程序在任一V之前启动主线程以外的线程 或W初始化,则未指定在哪个线程中 V和W的初始化发生;初始化没有顺序 如果它们出现在同一线程中。
    • 否则,初始化 V和W的顺序不确定。
  • [注意:此定义 允许同时初始化一系列有序变量 与另一个序列。 —尾注] ... 本节的其余部分介绍使用主线程和其他线程对初始化进行排序的方法。

无序动态初始化

第一个规则是相关的。请注意,显式实例化专业化显式专业化不同:


template<typename T>
struct A{
    static int x = foo();
}
// Rules are the same as for non-templates
template<>
struct A<char>{
    //C++17 inline definition
    inline static int x = foo();
    // Only declaration, must be defined in some translation unit
    static int y;
}

//Explicit instantion of `A` class template's `double` specialization.
template class A<double>;

tempalte<> struct A<char>::y=foo();
int main(){
    // Implicit instantiation of `A` class template's `int` specialization.
    A<int> v1;

    // Implicit instantiation of `A` class template's explicit `int` specialization.
    A<char> va2;
}

订单:

  • A<int>::x, A<double>::x, A<char>::y对于所有其他动态初始化,包括它们自身,都是无序的。
  • A<char>::y与其他有序变量在其定义的同一转换单元中有序。没有^三个变量(因为它们是无序的)而没有排序。

相同的规则适用于变量模板。

部分排序的初始化

第一条规则说,偏序适用于非模板类中的C ++ 17 inline静态变量。第二和第三条规则定义了顺序。 Cppreference可以很好地总结它。

[旁注 静态成员变量的C ++ 17 inline属性允许在类定义中立即定义和初始化变量,而无需程序员选择必须在其中放置定义的常用转换单元(.cpp)。分别。这导致了无数的SO问题,而作者却忘记这样做。最初,这是强制执行One Definition Rul的这种方式,但是上面的示例已经允许使用模板“破坏”它。在这种情况下,无论为同一A<T>::x实例化多少A<T>的TU,编译器都必须正确定义T。因此,C ++ 17对非模板允许相同的“违反”。处理多个与源代码相同的内联静态成员变量定义是编译器(尽管是链接程序)的责任。 ]

让我们举个例子:

class B{
   inline static int b = foo();
};
class C{
   inline static int c = foo();
};

a,b的初始化仅在出现两个类定义的所有翻译单元中以相同顺序出现时才进行排序。

此外,第三条规则将其他有序变量的顺序与源代码中出现的顺序相同:

#include "B.h"

static int global = foo();

#include "C.h"

如果有多个具有这种结构的TU,则初始化顺序应为:

  1. B::b
  2. 所有global变量均未定义顺序。
  3. C::c

考虑到这一点,我相信这会迫使C ++编译器创建具有“先于”关系的有向图,检查其是否具有拓扑顺序并按这种顺序初始化变量。

有序初始化

适用于所有其他非局部变量:

  1. 非内联静态成员,
  2. “普通”全局静态变量。

即正是由ODR控制的变量意味着初始化必须只在一个翻译单元中出现。

  • 完全按照它们在源代码中出现的顺序将它们初始化为同一TU中的其他变量。
  • 它们与其他TU的变量无序。