以下示例程序使用线程局部模板变量,并使用匿名名称空间中的类型实例化该变量。这会在gcc
中产生链接器错误。在clang
中,它实际上可以编译,但不能正确运行。
#include <stdio.h>
template<typename T>
struct container {
container() { printf("construct: %p\n", this); }
~container() { printf("destruct: %p\n", this); }
};
template<typename T>
thread_local container<T> storage;
namespace {
struct bar {};
}
int main() {
auto& ref1 = storage<bar>;
auto& ref2 = storage<bar>;
}
在gcc上,此操作失败并出现链接错误:
a.cpp:6:2: warning: ‘container<T>::~container() noexcept [with T = {anonymous}::bar]’ used but never defined
~container() { printf("destruct: %p\n", this); }
^
/tmp/ccgh0P15.o: In function `__tls_init':
a.cpp:(.text+0xa9): undefined reference to `container<(anonymous namespace)::bar>::~container()'
使用clang程序可以编译,但是会产生令人不安的输出:
construct: 0x7fb8d1c003d0
construct: 0x7fb8d1c003d0
destruct: 0x7fb8d1c003d0
destruct: 0x7fb8d1c003d0
我在这里遇到的围绕thread_local / templated变量是否存在一些粗糙的边缘?我不清楚为什么这会产生链接错误(如果删除thread_local,链接错误会消失)。在clang的情况下,具有静态存储持续时间的对象的双重破坏怎么可能只是编译器错误?在clang中,如果我删除匿名名称空间或thread_local规范,或者通过删除main()中的引用之一,问题就会消失。
任何人都可以阐明这一点,我主要是想知道我是否误解了什么。谢谢!
编辑:
clang++ -v
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
g++ -v
gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)