Clang/LLVM Windows 上的 7 和 8 多次初始化内联静态数据成员(link.exe 和 lld-link.exe)

Clang/LLVM 7 and 8 on Windows initialize inline static data member multiple times (with both link.exe and lld-link.exe)

Clang/LLVM Windows 上的 7 和 8 每个 TU 初始化一个内联静态数据成员一次。据我了解 C++17 这是不正确的。

尽管可以在多个 TU 中定义一个内联变量,编译器 and/or 链接器必须确保它在程序中只存在一次,因此 恰好初始化一次

以下小程序显示了 Clang/LLVM 发生的情况(在 Visual Studio 2017 和 2019 RC 中使用 LLVM 编译器工具链扩展测试):

// header.h

#include <iostream>

struct A
{
  A()      { std::cout << "ctor " << this << std::endl; }
  ~A()     { std::cout << "dtor " << this << std::endl; }
  void f() { std::cout << "f " << this << std::endl;  }
};

struct S
{
  inline static A a; // C++17 inline variable, thus also a definition 
};

// TU1.cpp

#include "header.h"

int main()
{
  S::a.f();
}

// TU2.cpp

#include "header.h"

// TU3.cpp

#include "header.h"

// TU4.cpp

#include "header.h"

这个程序打印:

ctor 010D4020
ctor 010D4020
ctor 010D4020
ctor 010D4020
f 010D4020
dtor 010D4020
dtor 010D4020
dtor 010D4020
dtor 010D4020

那是 四个 初始化 A 的唯一对象(实际上每个 TU 一个)而不是 恰好一个 (如 C ++17 项要求)。

程序应该打印:

ctor 010D4020
f 010D4020
dtor 010D4020

顺便说一句,这就是 MSVC 所做的。

这是 clang/LLVM 中的错误,对吗?

inline关键字的主要特点是它以两种方式修改 ODR 规则:

  1. 多个定义(有一些限制)是allowed

  2. 生成的对象被“折叠”成 single instance:

    An inline function or variable with external linkage shall have the same address in all translation units.

C++17 中唯一的补充是它还 allows 一个 static 数据成员声明作为定义。就是这样。

A static 数据成员仍然具有相同的 链接 external in your case), storage duration and lifetime,并且对于所有实际目的来说,就像全局定义的变量一样工作。 见 [class.static.data]/6:

Static data members are initialized and destroyed exactly like non-local variables

这基本上意味着它应该像这样工作:

struct A
{
  A()      { std::cout << "ctor "; }
  ~A()     { std::cout << "dtor "; }
};

A a; // in one of the TU's

extern A a; // in all other TU's

结论:

这是 Clang 中的一个错误。 static S::a 必须初始化并销毁一次。

This bug is fixed 当前快照构建基于 SVN r361807.