Thread_local variables aren't destroyed when a thread returns

Question

Does the behavior of the following code meet the standards? If so, why? When does the thread exit?

#include <iostream>
#include <thread>

struct tls_test {
    tls_test()
    { std::cout << "tls_test ctor\n"; }

    ~tls_test()
    { std::cout << "tls_test dtor\n"; }

    void print() const
    { std::cout << "tls_test print\n"; }
};

thread_local tls_test t;

void thread_1()
{
    std::cout << "thread_1 started\n";
    t.print();
    std::cout << "thread_1 return\n";
}

int main()
{
    std::thread trd{ thread_1 };
    trd.join();
    std::cout << "main return\n";
}

The given code produces the following output:

thread_1 started
tls_test ctor
tls_test print
thread_1 return
main return

According to [basic.stc.thread]/2, the variable t is constructed and shall be destroyed on thread exit. Does this behavior meet the standards? If so, why? When does the thread exit?

Yes, the behavior of the code meets the standards.

The thread exits after the thread_1 function completes execution and returns. In this case, the thread exits after printing “thread_1 return”.

The variable t is declared as thread_local, which means it has thread storage duration and each thread will have its own instance of t. According to basic.stc.thread/2 in the C++ standard, a thread_local variable is constructed when the thread starts and is destroyed when the thread exits.

In the given code, the constructor of tls_test is called when the thread starts (“tls_test ctor”), and the destructor is called when the thread exits (“tls_test dtor”). The output confirms this behavior.