https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66360
--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> --- This has nothing to do with thread_local. That is removing static thread_local still causes it to produce an error. Here is a more reduced testcase: template <typename T> struct wrapper final { T value; template <typename ...Args> wrapper(Args &&...args) : value(args...) { } }; struct non_copyable { non_copyable(const non_copyable &) = delete; non_copyable(); ~non_copyable(); }; template <typename T> class thread_local_variable final { private: wrapper<T> *pointer; public: template <typename ...Args> thread_local_variable(Args &&...args) { wrapper<T> v(args...); pointer = &v; } T &get() { return pointer->value; } }; non_copyable &fn() { thread_local_variable<non_copyable> v; return v.get(); } --- CUT ---- args here is {}. I suspect an empty arguments it not being treated correctly.