es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

C++: Redireccionamiento de llamadas a funciones miembro mediante estructuras de datos variádicas.

Estoy tratando de crear una clase (similar a std::tuple) que pueda contener un número variable de referencias a objetos de diferentes tipos y que pueda reenviar una llamada a una función miembro específica a todos sus constituyentes. Aquí hay un ejemplo:

“` c++
// g++ Test7.C -std=c++17 -o Test7

<h1>include <iostream></h1>

<p>struct P1 { void operator()(void) const { std::cout<<"P1 "; } };
struct P2 { void operator()(void) const { std::cout<<"P2 "; } };
struct P3 { void operator()(void) const { std::cout<<"P3 "; } };</p>

<p>template <class… EmitterList> struct Emitters
{
inline void operator()(void) const { std::cout<<std::endl; }
};</p>

<p>template <class Emitter, class… EmitterList>
class Emitters<Emitter, EmitterList…> : public Emitters<EmitterList…>
{
public:
using Base = Emitters<EmitterList…>;</p>

<dl>
<dt>Emitters(const Emitter & e, const EmitterList & … eList)</dt>
<dd>emitter(e), Base(eList…) {}</p></dd>
</dl>

<p><p>inline void operator()(void) const { emitter(); this->Base::operator()(); }
private:
const Emitter & emitter;
};</p>

<p>int main(void)
{
P1 p1;
P2 p2;
P3 p3;
Emitters e0; e0(); // funciona
//Emitters e1{p1}; e1(); // no funciona
//Emitters e2{p1,p2}; e2(); // no funciona
//Emitters e3{p1,p2,p3}; e3(); // no funciona
return 0;
}
“`
La expectativa es que e1() emitiría “P1 \n” (llama a p1()), e2() emitiría “P1 P2 \n” (llama a p1(); p2()), y e3() emitiría “P1 P2 P3 \n” (llama a p1(); p2(); p3();). Pero algo no está bien en el código: se compila para e0 pero no para los otros casos. ¿Podría ayudarme a entender qué es lo que estoy haciendo mal aquí y cómo solucionarlo?

¡Muchas gracias por su ayuda!

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con el código es que el constructor de Emitters está tomando los argumentos por referencia constante. Ya que los argumentos pasados durante la instanciación de e1, e2 y e3 son temporales, está causando un error de referencia a un temporal.

    Para solucionar esto, necesitamos cambiar el constructor para que tome los argumentos por valor y luego use el perfect forwarding para reenviar los argumentos al constructor de la clase base. Podemos hacer esto utilizando un constructor variádico:

    “`c++
    template <class… emitters=””>
    class Emitters
    {
    public:
    template <class… args=””>
    Emitters(Args&&… args)
    : emitters(std::forward(args)…) {}

    inline void operator()() const { std::cout<<std::endl; }="" private:=""></std::endl;><emitters...> emitters;
    

    };


    Esto nos permitirá crear objetos como `Emitters e1{p1};` sin problemas. Aquí está el código actualizado: ```c++ #include <iostream> #include <tuple> struct P1 { void operator()(void) const { std::cout<<"P1 "; } }; struct P2 { void operator()(void) const { std::cout<<"P2 "; } }; struct P3 { void operator()(void) const { std::cout<<"P3 "; } }; template <class... emitters=""> class Emitters { public: template <class... args=""> Emitters(Args&&... args) : emitters(std::forward<args>(args)...) {} inline void operator()() const { std::cout<<std::endl; }="" private:=""></std::endl;><emitters...> emitters; }; int main(void) { P1 p1; P2 p2; P3 p3; Emitters<> e0; e0(); // funciona Emitters e1{p1}; e1(); // funciona Emitters e2{p1,p2}; e2(); // funciona Emitters e3{p1,p2,p3}; e3(); // funciona return 0; }

    Ahora todos los objetos funcionan correctamente y producen la salida esperada.</emitters…></class…></class…></emitters…></class…></class…>

Comments are closed.