模板中thread_local相关的Clang编译器疑似Bug咨询
Let's break down what's happening here and address your question directly.
First, let's recap your scenario with the relevant code and behavior:
Your Simplified Code
#include <iostream> #include <unordered_map> #include <vector> template<class T> class ThreadLocalMember { public: T& local() { return store.map[this]; } private: struct Store { Store() { std::cout << "construct" << std::endl; } ~Store() { std::cout << "destruct" << std::endl; } std::unordered_map<ThreadLocalMember<T>*, T> map; }; static thread_local Store store; }; template <class T> thread_local typename ThreadLocalMember<T>::Store ThreadLocalMember<T>::store; int main() { ThreadLocalMember<int> counter; std::cout << "point 1" << std::endl; int result = counter.local(); std::cout << "point 2; result: " << result << std::endl; return result; }
Expected Output
point 1 construct point 2; result: 0 destruct
Actual Output (Clang on MacOS with -O1/-O2/-O3)
point 1 Illegal instruction: 4
Analysis & Conclusion
This is almost certainly a Clang compiler bug on MacOS when optimizing template classes with thread_local static members. Here's why:
Per the C++14 standard, thread_local static variables must be initialized the first time they are accessed by a thread. In your code, calling counter.local() should trigger the initialization of ThreadLocalMember<int>::store (and thus run the Store constructor) before accessing store.map.
GCC correctly handles this initialization even with optimizations enabled, but Clang (specifically Apple LLVM 9.1.0) appears to optimize away the initialization step when the thread_local member is part of a template class. The "Illegal instruction" crash happens because you're accessing the unordered_map in an uninitialized Store instance—its internal structures are garbage, leading to invalid memory operations.
Your observed workarounds confirm this:
- Disabling optimizations removes the aggressive logic that breaks initialization.
- Using a non-template class works because Clang handles non-template
thread_localstatic members correctly. - Removing
thread_localeliminates the thread-specific initialization logic entirely (but obviously breaks your multi-threading requirements).
Workaround
A simple fix to force Clang to initialize the store member is to explicitly reference it before accessing the map, which prevents the optimizer from eliding the initialization:
T& local() { (void)store; // Explicitly reference store to trigger construction return store.map[this]; }
This forces the compiler to initialize store before accessing map, which should resolve the crash while keeping your thread-local behavior intact.
内容的提问来源于stack exchange,提问作者Peter Camps

