I am trying to understand the constructor of a std::thread
but fail to understand how parameter types are represented/handled. Judging from cppreference, a simplified constructor could be sketched as follows:
class thread {
public:
template <class Function, class Arg>
thread(Function&& f, Arg&& arg) {
// something happening
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Arg>(arg)));
// something else happening
}
};
cppreference defines decay_copy
as:
template <class T>
std::decay_t<T> decay_copy(T&& v) { return std::forward<T>(v); }
I experimented a bit with the following examples:
struct X{};
int main() {
X x1{};
X& x2 = x1;
auto f = []() { return; };
thread t1{f, x1}; // arg should be of type X& after the inner std::forward<Arg>(arg);
thread t2{f, x2}; // arg should be of type X& after the inner std::forward<Arg>(arg);
thread t3{f, X{}}; // arg should be of type X&& after the inner std::forward<Arg>(arg);
}
According to my analysis, both x1
and x2
are of type lvalue reference after the inner std::forward
whileX{}
is of type rvalue reference. I believe we somehow need to separate x1
and x2
to pass it by value or by reference. The analysis leads me to three questions:
- Is the analysis above correct?
- How does
decay_copy
untangle the types correctly? - After starting at this for some time, I wonder: Oh bother, why is this so involved? Could it be done easier? The answer is, of course, no, but I still lack intuition for the entire operation.
I am grateful for any hints, suggestions, or explanations!
Aucun commentaire:
Enregistrer un commentaire