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!
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 dee1
,e2
ye3
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++(args)…) {}
template <class… emitters=””>
class Emitters
{
public:
template <class… args=””>
Emitters(Args&&… args)
: emitters(std::forward
};
Ahora todos los objetos funcionan correctamente y producen la salida esperada.</emitters…></class…></class…></emitters…></class…></class…>