C++: ¿Puntero a la versión monomórfica de la función miembro virtual?


En C++, es posible obtener un puntero a una función miembro (no estática) de una clase, y luego invocarla en un objeto. Si la función era virtual, la llamada se envía dinámicamente dependiendo del tipo dinámico del objeto. También es posible (sin usar un puntero miembro) llamar a funciones miembro virtuales de objetos de forma monomórfica, proporcionando explícitamente el ámbito que contiene la versión a usar. El siguiente código demuestra esto:

#include <iostream>
using std::cout; using std::endl;

struct Foo
{
    virtual void foo() { cout << 1 << endl; }
};

struct Foo2: public Foo
{
    virtual void foo() { cout << 2 << endl; }
};

int main( int, char** )
{
    Foo *foo = new Foo2;

    void (Foo::*foo_pointer)() = &Foo::foo;

    foo->foo();            // prints 2
    foo->Foo::foo();       // prints 1
    (foo->*foo_pointer)(); // prints 2
}

Lo que me gustaría hacer es combine los dos, y obtenga un puntero a la versión monomorfa de una función miembro; es decir, quiero un puntero a Foo::foo que siempre llame a la versión de la clase base de foo, e imprima 1, incluso si se invoca en un Foo2. Sin embargo, no he sido capaz de encontrar una manera de hacer esto. Es posible?

(Aparte de la tediosa forma manual de escribir una nueva función no virtual que hace la llamada monomórfica, y luego obtener un puntero a eso.)

Author: glaebhoerl, 2011-02-21

3 answers

Es posible en GCC, pero la forma en que está documentado en la sección Extensiones del lenguaje C++ sugiere que no hay una forma portátil de hacerlo.

Puedes hacer dos cosas:

  1. Si controla la clase, cree una función no virtual y una envoltura virtual para ella y cuando sepa que no necesita envío virtual, simplemente tome la dirección de la no virtual.
  2. Si no lo hace, cree un funtor de plantilla que contendrá el puntero del miembro y hará el ámbito explícito llamada.
 9
Author: Jan Hudec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-21 15:51:27

En otras palabras : quieres hacer trampa.

No, no es posible porque así es como funciona el polimorfismo en combinación con el método puntero a miembro.

 0
Author: BЈовић,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-21 15:08:32

Para elaborar con un ejemplo de código para una función wrapper (y a pesar del hecho de que el OP quería evitar este método!) como en muchos casos esta es la solución pragmáticamente preferible:

#include <iostream>
using std::cout; using std::endl;

struct Foo
{
    virtual void foo() { cout << 1 << endl; }
};

struct Foo2: public Foo
{
    virtual void foo() { cout << 2 << endl; }
};

void monomorphicFooFoo( Foo * f ) { f->Foo::foo(); }

int main()
{
    Foo *foo = new Foo2;

    void (*baseFoo)( Foo * ) = &monomorphicFooFoo;
    baseFoo( foo ); // Prints 1
}
 0
Author: SimonD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-21 08:41:19