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 规则:
多个定义(有一些限制)是allowed
生成的对象被“折叠”成 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.
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 规则:
多个定义(有一些限制)是allowed
生成的对象被“折叠”成 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.