std::thread::thread(3)
π std::thread::thread β from the C++ Standard Library manual, bestie
Okay so this page is literally all about std::thread::thread, the constructor of the thread class, and it's from the C++ Standard Library section β welcome to the group chat π«Ά
β¨ NAME β¨
std::thread::thread β yeah it's the constructor, it's the main character, it constructs threads π
π¦ SYNOPSIS π¦
Here's the whole lineup, four overloads, no notes:
thread() noexcept; (1) (since C++11)
thread( thread&& other ) noexcept; (2) (since C++11)
template< class F, class... Args > (3) (since C++11)
explicit thread( F&& f, Args&&... args );
thread( const thread& ) = delete; (4) (since C++11)
Constructs a new std::thread object. Slay β¨
πΈ DESCRIPTION πΈ
Overload (1) β the empty one
Creates a new std::thread object which does not represent a thread. It's giving "placeholder queen" β it exists, it just has no thread behind it π
Overload (2) β the move constructor
Move constructor. Constructs the std::thread object to represent the thread of execution that was represented by other. After this call other no longer represents a thread of execution. So other is literally left with nothing β not me stealing your thread π
Overload (3) β the real one, the icon
Creates a new std::thread object and associates it with a thread of execution. The new thread of execution starts executing:
INVOKE(decay-copy(std::forward<F>(f)), (until C++23)
decay-copy(std::forward<Args>(args))...)
std::invoke(auto(std::forward<F>(f)), (since C++23)
auto(std::forward<Args>(args))...)
The calls of decay-copy are evaluated (until C++23) / the values produced by auto are materialized (since C++23) in the current thread, so that any exceptions thrown during evaluation and copying/moving of the arguments are thrown in the current thread, without starting the new thread. Translation: the error happens on your thread before the new one even gets out of bed π
This overload participates in overload resolution only if std::decay<F>::type (until C++20) / std::remove_cvref_t<F> (since C++20) is not the same type as std::thread.
If any of the following conditions is satisfied, the program is ill-formed:
Fis notMoveConstructible. (until C++20)- Any type in
Argsis notMoveConstructible. INVOKE(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)is not a valid expression.
If any of the following is false, the program is ill-formed (since C++20):
std::is_constructible_v<std::decay_t<F>, F>(std::is_constructible_v<std::decay_t<Args>, Args> && ...)std::is_invocable_v<std::decay_t<F>, std::decay_t<Args>...>
The completion of the invocation of the constructor synchronizes with the beginning of the invocation of the copy of f on the new thread of execution. (Synchro, but make it threading π§)
Overload (4) β deleted, we don't do that here
The copy constructor is deleted; threads are not copyable. No two std::thread objects may represent the same thread of execution. Periodt. One thread, one owner, that's the vibe check π
π PARAMETERS π
otherβ another thread object to construct this thread object withfβCallableobject to execute in the new threadargsβ arguments to pass to the new function
π POSTCONDITIONS π
- (1)
get_id()equal tostd::thread::id()(i.e.joinable()isfalse). - (2)
other.get_id()equal tostd::thread::id()andget_id()returns the value ofother.get_id()prior to the start of construction. - (3)
get_id()not equal tostd::thread::id()(i.e.joinable()istrue).
π₯ EXCEPTIONS π₯
- (3)
std::system_errorif the thread could not be started. The exception may represent the error conditionstd::errc::resource_unavailable_try_againor another implementation-specific error condition. Not the vibe, but good to know π
β NOTES β
The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g., with std::ref or std::cref). Learn it, live it, love it β this is the #1 thing people get wrong, fr fr π
Any return value from the function is ignored. If the function throws an exception, std::terminate is called. In order to pass return values or exceptions back to the calling thread, std::promise or std::async may be used. So no, you can't just return from a thread and expect it back β use the right tool bestie π
π EXAMPLE π
// Run this code
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
void f1(int n)
{
for (int i = 0; i < 5; ++i)
{
std::cout << "Thread 1 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i)
{
std::cout << "Thread 2 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
class foo
{
public:
void bar()
{
for (int i = 0; i < 5; ++i)
{
std::cout << "Thread 3 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
class baz
{
public:
void operator()()
{
for (int i = 0; i < 5; ++i)
{
std::cout << "Thread 4 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
int main()
{
int n = 0;
foo f;
baz b;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no
longer a thread
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
std::thread t6(b); // t6 runs baz::operator() on a copy of object b
t2.join();
t4.join();
t5.join();
t6.join();
std::cout << "Final value of n is " << n << '\n';
std::cout << "Final value of f.n (foo::n) is " << f.n << '\n';
std::cout << "Final value of b.n (baz::n) is " << b.n << '\n';
}
Possible output:
Thread 1 executing
Thread 2 executing
Thread 3 executing
Thread 4 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Thread 4 executing
Thread 2 executing
Thread 3 executing
Thread 1 executing
Thread 4 executing
Thread 3 executing
Thread 2 executing
Thread 1 executing
Thread 4 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Thread 4 executing
Final value of n is 5
Final value of f.n (foo::n) is 5
Final value of b.n (baz::n) is 0
(Yes the order looks chaotic β it's threads, that's the whole point, the interleaving is a β¨possibleβ¨ output, not a promise π)
π§ DEFECT REPORTS π§
The following behavior-changing defect reports were applied retroactively to previously published C++ standards. So old standard, new rules β glow-up via paperwork π
- LWG 2097 β applied to C++11. Behavior as published: for overload (3),
Fcould bestd::thread. Correct behavior:Fis constrained. - LWG 3476 β applied to C++20. Behavior as published: overload (3) directly required (the decayed types of)
Fand the argument types to be move constructible. Correct behavior: removed these requirements^[1].
- ^ The move-constructibility is already indirectly required by
std::is_constructible_v.
π REFERENCES π
- C++23 standard (ISO/IEC 14882:2023):
33.4.3.3thread constructors[thread.thread.constr]
- C++20 standard (ISO/IEC 14882:2020):
32.4.2.2thread constructors[thread.thread.constr]
- C++17 standard (ISO/IEC 14882:2017):
33.3.2.2thread constructors[thread.thread.constr]
- C++14 standard (ISO/IEC 14882:2014):
30.3.1.2thread constructors[thread.thread.constr]
- C++11 standard (ISO/IEC 14882:2011):
30.3.1.2thread constructors[thread.thread.constr]
π SEE ALSO π
constructorβ constructs newjthreadobject (public member function ofstd::jthread)- C documentation for
thrd_create
Okay that's the whole page, no cap π Big shoutout to cppreference.com, the 2024.06.10 edition β that's the source and date of this iconic little document, so go give it a kiss on the forehead for me. Bye bestie, go join() your threads πβ¨
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms