...
Le quite el simbolo de referencia en el parámetro para la funcion de la linea 5 y ahí compiló..
Pero no veo por qué...
Para poner las cosas más a mano, quitando de tu ejemplo lo que no viene al caso:
#include <iostream>
#include <thread>
#include <vector>
void f(std::vector<double>& vd) {
for (auto& v : vd)
std::cout << "From f -> " << v << std::endl;
}
int main() {
std::vector<double> vd{ 2.5, 5.5, 7.1 };
std::thread t0{ f, vd }; //<-- Error : No type named type in clas std::result_of))(std::vector&)> Si comento esta linea compila...
t0.join();
return 0;
}
Y dices que si quitas la referencia, si la función "f" toma su argumento por valor, entonces compila sin error. Está bien, y quisieras saber por qué...
Porque el constructor de thread que estás invocando es:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
Ver:
https://en.cppreference.com/w/cpp/thread/thread/threadY en ese caso, en las NOTAS, se aclara que
"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). "
NO es que lo esté aclarando, sólo se dice que "
Así debe ser",
Entonces, en tu ejemplo, podrías poner:
std::thread t0{ f, std::ref(vd) };
Funciona así porque std::ref devuelve ese wrapper que necesitas en el segundo argumento template, que se pasa por valor, pero se comporta como una referencia (encapsula, contiene, envuelve una referencia).
https://en.cppreference.com/w/cpp/utility/functional/refPero preguntabas "por qué".
Bueno, porque al construir la instancia de thread, en su constructor se invoca a la función del primer parámentro, con los parámetros del segundo parámetro, ¿de acuerdo?, pero esa llamada a f se hace a través de
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
Donde se invoca la función f con el "parameter pack" que devuelva "decay_copy", ¿verdad?
Pero esa "decay_copy" sólo hace un "forward" del pack que recibe, y devuelve a su vez un std::decay_t.
Y, ajá, acá está, es eso. Si te fijas en la documentación sobre std::dacay_t:
https://en.cppreference.com/w/cpp/types/decay verás que para este caso:
the member typedef type is std::remove_cv<std::remove_reference<T>::type>::type.
These conversions model the type conversion applied to all function arguments when passed by value.
Ahorá sí, no sólo está aplicando std::remove_cv, que no tiene que ver con lo nuestro en este momento, sino que, fundamentalmente, aplica std::remove_reference al T::type que recibe como parámetro, que eso sí termina de explicar por qué necesitamos un wrapper cuando la función que se va a ejecutar en el nuevo thread toma sus parámetros por referencia.
Quise editar mi respuesta anterior y quedó una completa basura, así que escribo aquí a modo de apéndice:Otra variante para considerar es que el parámetro de f sea una
const reference:
void f(const std::vector<double>& vd)
o, como hay quien prefiere:
void f(std::vector<double> const& vd)
(con el "const" antes de &)