std::thread pass by reference calls copy constructor
std::thread
takes its arguments by value. You can get reference semantics back by using std::reference_wrapper
:
std::thread newThread(session, &sock, std::ref(logger));
Obviously you must make sure that logger
outlives the thread.
std::thread in constructor referencing it's deleted copy constructor?
You have to std::move()
the RenderThread
constructor's parameter into the constructor of the m_renderThread
member:
: m_renderThread(std::move(threadToGive)) {}
Start std::thread in member function of object that does not have a copy constructor
std::thread
use std::invoke
to call the function. std::invoke
is smart enough to be able to take a pointer to an object and call a member function with it. That means you can change t2
to
std::thread t2(&Dummy::print, &d);
and get the correct behavior.
Why is this constructor being called 3 times when passing to std::thread?
It's an implementation detail of the C++ standard library and is probably different in different implementations. There is more going on in std::thread t(takeWidget, widget)
than simply calling takeWidget(widget)
. For example, in the GCC implementation, the thread
constructor calls make_tuple()
which would move or copy the parameters depending on their types.
Copy constructor calls when creating a new thread
If you set breakpoint in copy constructor you can see constructor call context in Call Stack window. In debug mode i found next points when constructor is called:
First off the functional object is copied in helper function
bind
Then the functional object is moved into a internal functional object
_Bind
After that a class for launching threads
_LaunchPad
is created. In
a constructor it takes rvalue reference to the _Bind instance so we have
another one move constructor callmove constructor of
_LaunchPad
is called when copy of it is created in new thread.
Thus we have 4 copy constructor call in your case. If you added move constructor you would see 1 copy constructor and 3 move constructor calls.
In release mode all empty constructor calls is elided and assembler code looks quite simple
std::thread initialization with class argument results with class object being copied multiple times
There are a lot of copying/moving going on in the background. Note however, that neither the copy constructor nor the move constructor is called when the thread constructor is called.
Consider a function like this:
template<typename T> void foo(T&& arg);
When you have r-value references to template arguments C++ treats this a bit special. I will just outline the rules here. When you call foo
with an argument, the argument type will be
- && - when the argument is an r-value
- & - all other cases
That is, either the argument will be passed as an r-value reference or a standard reference. Either way, no constructor will be invoked.
Now look at the constructor of the thread object:
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
This constructor applies the same syntax, so arguments will never be copied/moved into the constructor arguments.
The below code contains an example.
#include <iostream>
#include <thread>
class Foo{
public:
int id;
Foo()
{
id = 1;
std::cout << "Default constructor, id = " << id << std::endl;
}
Foo(const Foo& f)
{
id = f.id + 1;
std::cout << "Copy constructor, id = " << id << std::endl;
}
Foo(Foo&& f)
{
id = f.id;
std::cout << "Move constructor, id = " << id << std::endl;
}
};
void doNothing(Foo f)
{
std::cout << "doNothing\n";
}
template<typename T>
void test(T&& arg)
{
}
int main()
{
Foo f; // Default constructor is called
test(f); // Note here that we see no prints from copy/move constructors
std::cout << "About to create thread object\n";
std::thread t{doNothing, f};
t.join();
return 0;
}
The output from this code is
Default constructor, iCount = 1
About to create thread object
Copy constructor, id = 2
Move constructor, id = 2
Move constructor, id = 2
doNothing
- First, the object is created.
- We call our test function just to see that nothing happens, no constructor calls.
- Because we pass in an l-value to the thread constructor the argument has type l-value reference, hence the object is copied (with the copy constructor) into the thread object.
- The object is moved into the underlying thread (managed by the thread object)
- Object is finally moved into the thread-function doNothing's argument
Related Topics
How to Printf Uint64_T? Fails With: "Spurious Trailing '%' in Format"
Non-Blocking Worker - Interrupt File Copy
High Resolution Timer With C++ and Linux
Initializing a Two Dimensional Std::Vector
Should I Use an Exception Specifier in C++
Ternary Operator : VS If...Else
Static_Assert Dependent on Non-Type Template Parameter (Different Behavior on Gcc and Clang)
C++: What Regex Library Should I Use
Vectors, Structs and Std::Find
Explicit Specialization of Template Class Member Function
Macro For Dllexport/Dllimport Switch
What Are the Pointer-To-Member Operators -≫* and .* in C++
What New Capabilities Do User-Defined Literals Add to C++