Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: digimikeh en 4 Agosto 2019, 00:58 am



Título: Uso de Threads y error de compilación asignado a una libreria estandar...
Publicado por: digimikeh en 4 Agosto 2019, 00:58 am
Buenas..

Tengo este código y me está saliendo error, ppienso que la sintaxis está correcta.. aun no entiendo por qué el error..  Alguna pista?


Ver en el siguiente enlace el codigo para probar:
https://onlinegdb.com/ryO2ptQ7B (https://onlinegdb.com/ryO2ptQ7B)

Saludos.


Edit : 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é...


Título: Re: Uso de Threads y error de compilación asignado a una libreria estandar...
Publicado por: Loretz en 4 Agosto 2019, 21:17 pm
...
Citar
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:
Código
  1. #include <iostream>
  2. #include <thread>
  3. #include <vector>
  4.  
  5. void f(std::vector<double>& vd) {
  6.    for (auto& v : vd)
  7.        std::cout << "From f -> " << v << std::endl;
  8. }
  9.  
  10. int main() {
  11.    std::vector<double> vd{ 2.5, 5.5, 7.1 };
  12.  
  13.    std::thread t0{ f, vd };      //<-- Error : No type named type in clas std::result_of))(std::vector&)> Si comento esta linea compila...
  14.  
  15.    t0.join();
  16.  
  17.    return 0;
  18. }
  19.  

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:
Código:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
Ver: https://en.cppreference.com/w/cpp/thread/thread/thread (https://en.cppreference.com/w/cpp/thread/thread/thread)

Y en ese caso, en las NOTAS, se aclara que
Citar
"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:

Código
  1. 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/ref (https://en.cppreference.com/w/cpp/utility/functional/ref)


Pero 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
Código
  1. std::invoke(decay_copy(std::forward<Function>(f)),
  2.            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 (https://en.cppreference.com/w/cpp/types/decay) verás que para este caso:

Citar
    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:
Código:
void f(const std::vector<double>& vd)

o, como hay quien prefiere:
Código:
void f(std::vector<double> const& vd)
(con el "const" antes de &)