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++ std::function para tomar funciones con parámetro de subclase

[Actualización] Razón de esta pregunta:
Hay muchas lambdas existentes definidas como [](const ChildType1& child), todas en un gran registro. Queremos registrar nuevas lambdas como [](const ChildType2& child) en el mismo registro. Si definimos el envoltorio de función usando Parent, para las muchas lambdas existentes necesitamos cambiarlas a [](const Parent& someone) y realizar un downcast desde Parent a ChildType1 dentro.


Si tengo un envoltorio de función como std::function<void(const Parent&)>, ¿hay alguna manera de permitir que acepte una función con una subclase de Parent como parámetro, por ejemplo, [](const Child& child){...}, donde Child es una subclase de Parent.

Salgo de aquí no se compila. IDE en línea link.
“`

<h1>include <iostream></h1>

<h1>include <functional></h1>

class Parent {
public:
virtual void decir() const {
std::cout<<"Soy el padre"<<"\n";
}
};

class Child: public Parent {
public:
void decir() const {
std::cout<<"Soy el hijo"<<"\n";
}
};

typedef std::function<void(const Parent&)> Envoltorio;

int main() {
Envoltorio func=[](const Child& child){ // por supuesto, funciona si Child->Parent
child.decir();
};

<pre><code>Child c;
func(c);
return 0;
</code></pre>

}
“`

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Sí, es posible permitir que un envoltorio de función con std::function<void(const parent&)=""> tome una función con un subclase de Parent como parámetro. Esto se puede lograr utilizando plantillas de función y std::enable_if.

    #include <iostream>
    #include <functional>
    #include <type_traits>
    
    class Parent {
    public:
        virtual void decir() const {
            std::cout << "Soy el padre\n";
        }
    };
    
    class Child: public Parent {
    public:
        void decir() const {
            std::cout << "Soy el hijo\n";
        }
    };
    
    template<typename t="">
    using es_puntero_de_padre = std::enable_if_t<><parent,></parent,><t>>>;
    
    template<typename t="">
    using no_es_puntero_de_padre = std::enable_if_t<!std::is_base_of_v<Parent, std::remove_reference_t<T>>>;
    
    typedef std::function<void(const parent&)=""> Envoltorio;
    
    int main() {
        Envoltorio funcion = [](const Parent& p){
          p.decir();
        };
    
        Child c;
        funcion(c);
    
        // Prueba con una subclase no Parent
        // Descomenta abajo para ver el error de compilación
        //funcion(std::string{"hola"});
    
        // Prueba con una subclase Child con plantilla
        // Descomenta abajo para ver el error de compilación
        //funcion_con_child(Child{});
    
        return 0;
    }
    
    // Plantilla de función que solo acepta subclases de Parent como parámetro
    template<typename t,="" typename=""></typename><t>>
    void funcion_con_child(T& t) {
        t.decir();
    }
    
    // Plantilla de función que solo acepta no subclases de Parent como parámetro
    template<typename t,="" typename=""></typename><t>>
    void funcion_sin_child(T& t) {
        std::cout << "No es un hijo de Parent\n";
    }
    

    En el ejemplo anterior, definimos dos plantillas de función funcion_con_child y funcion_sin_child que aceptan un parámetro de tipo T. es_puntero_de_padre y no_es_puntero_de_padre se utilizan como alias para std::enable_if para imponer las restricciones de tipo.

    En la función principal, podemos pasar un objeto Child a la función Envoltorio, que llamará al método decir de Child.

    Ten en cuenta que pasar cualquier subclase no Parent resultará en un error de compilación, ya que la función Envoltorio está diseñada solo para aceptar Parent o sus subclases.</void(const></void(const>

Comments are closed.