C ++: указатель на мономорфную версию виртуальной функции-члена?

В C ++ можно получить указатель на (нестатическую) функцию-член класса, а затем вызвать его для объекта. Если функция была виртуальной, вызов отправляется динамически в зависимости от динамического типа объекта. Также возможно (без использования указателя на член) мономорфно вызывать виртуальные функции-члены объектов, явно предоставляя область, содержащую версию для использования. Следующий код демонстрирует это:

#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
}

Я хотел бы объединить их и получить указатель на мономорфную версию функции-члена; т.е. мне нужен указатель на Foo :: foo, который всегда вызывает версию foo базового класса и печатает 1, даже если он вызывается в Foo2. Однако мне не удалось найти способ сделать это. Является ли это возможным?

(За исключением утомительного ручного способа написания новой невиртуальной функции, которая выполняет мономорфный вызов, а затем получает указатель на это.)


person glaebhoerl    schedule 21.02.2011    source источник
comment
Новый пользователь SO задает хороший вопрос с компилируемыми фрагментами кода? Теперь я не вижу этого каждый день! :-)   -  person In silico    schedule 21.02.2011
comment
Даже довольно сложный вопрос.   -  person Alexandre C.    schedule 21.02.2011
comment
Интересно, зачем вам это нужно, и нет ли лучшего способа добиться того, чего вы хотите достичь с помощью этого. (О, и я не думаю, что это возможно, но C ++ продолжает меня удивлять, поэтому я не буду ляпнуть, говоря об этом.)   -  person sbi    schedule 21.02.2011
comment
@sbi Я работаю над языковыми привязками и имею структуру указателей функций, соответствующих виртуальным функциям класса (фактически вторая vtable). Сам класс становится подклассом, так что каждая виртуальная функция вызывает эту «vtable». В случае, если виртуальная функция не была переопределена с другого языка, я хочу, чтобы запись указывала на исходную версию функции, но это должна быть мономорфная версия, иначе единственным результатом будет бесконечный цикл. (и, э-э, переполнение стека).   -  person glaebhoerl    schedule 21.02.2011
comment
@illissius: рассматривали ли вы возможность использовать лямбды или std::function для этого? GCC и VC10 поддерживают их.   -  person sbi    schedule 21.02.2011
comment
@sbi: хм, это хорошая идея - если это невозможно сделать напрямую, это все равно определенно лучше, чем писать совершенно новую функцию «верхнего уровня», чтобы делать это вручную. Благодарю.   -  person glaebhoerl    schedule 21.02.2011
comment
@In silico: потому что большинство людей, способных задать хороший вопрос с помощью компилируемых фрагментов кода, уже являются членами SO!   -  person Steve Jessop    schedule 21.02.2011
comment
@illissius: Как вы поймете, была ли виртуальная функция переопределена с другого языка или нет?   -  person ali_bahoo    schedule 21.02.2011
comment
@sad_man: ну, это ответственность другого языка :). (для записи, другой язык - Haskell, и в настоящее время ответ - либо пользователь так говорит, либо злые, но эффективные хаки.)   -  person glaebhoerl    schedule 21.02.2011


Ответы (3)


Это возможно в GCC, но так, как это задокументировано на языке C ++ раздел расширений предполагает, что нет портативного способа сделать это.

Вы можете сделать две вещи:

  1. Если вы управляете классом, создайте для него невиртуальную функцию и виртуальную оболочку, а когда вы знаете, что вам не нужна виртуальная диспетчеризация, просто возьмите адрес невиртуальной функции.
  2. Если вы этого не сделаете, создайте функтор шаблона, который будет содержать указатель на член и выполнять явный вызов области видимости.
person Jan Hudec    schedule 21.02.2011
comment
Указатели на связанные функции (обычно называемые делегатами) - это нечто совершенно иное. - person Ben Voigt; 21.02.2011
comment
Нет, похоже, это именно то, что я искал (и согласен с тем, что если это расширение GCC, вероятно, нет соответствующего стандартам способа сделать это). Это не имеет отношения к делегатам; он преобразует void (Foo :: *) (), который проходит через vtable, в void (*) (Foo *), чего нет. Что, опять же, именно то, что я хотел. Большое спасибо! - person glaebhoerl; 22.02.2011

Другими словами: вы хотите обмануть.

Нет, это невозможно, потому что именно так работает полиморфизм в сочетании с методом указателя на член.

person BЈовић    schedule 21.02.2011
comment
это способ работы полиморфизма. Как демонстрирует спрашивающий, можно сделать желаемый обман с помощью синтаксиса foo->Foo::foo();. Просто указатели на функции-члены работают не так. - person Steve Jessop; 21.02.2011
comment
@ Стив. Ты прав. Я изменю свой ответ. Но настоящий ответ - комбинация двух: так работает полиморфизм в сочетании с методами указателя на член. - person BЈовић; 21.02.2011

Чтобы уточнить пример кода для функции-оболочки (и несмотря на то, что OP хотел избежать этого метода!), Поскольку во многих случаях это прагматически предпочтительное решение:

#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
}
person SimonD    schedule 21.04.2015