Вызов квалифицированного идентификатора базовой функции через указатель

Если у меня есть виртуальная функция foo(), сначала определенная в базовом классе B, а затем переопределенная в производном классе D, как я могу сохранить адрес B::foo в функции-указателе таким образом, чтобы при ее вызове вызов будет вести себя как вызов с квалифицированным идентификатором (например, pd->B::foo())?

Пример:

struct B {
    virtual int foo() { return 1; }
};

struct D: public B {
    virtual int foo() { return 2; }
};

int main(int argc, char * argv[]) {
    D* pd = new D();
    int (B::*pf)() = &B::foo;
    int r = (pd->*pf)();
    return 0; 
}

Это вызовет D::foo(). Могу ли я инициализировать pf таким образом, чтобы (pd->*pf)() вызывал B::foo(), даже если динамический тип pd является классом, который переопределяет foo()?

(Прежде чем кто-нибудь спросит, я действительно не хочу этого делать, мне просто любопытно, возможно ли это.)


person imre    schedule 10.12.2014    source источник
comment
Дешевый хак: int (*fp)(B &) = [](B & b) { return b.B::foo(); };   -  person Kerrek SB    schedule 10.12.2014


Ответы (4)


Я не верю, что вы можете.
У меня нет с собой своего стандарта, но я использую варарг-хак для вывода значений указателя на функции-члены http://ideone.com/bRk7mG:

#include <iostream>
#include <cstdarg>
using namespace std;

struct Test
{
    void foo() {};
    virtual void bar() {}; 
    virtual void bar2() {};
    virtual void bar3() {};
};

void print_hack(int dummy, ...)
{
    va_list argp;
    va_start(argp, dummy);
    long val = va_arg(argp, long);
    cout << val << endl;
    va_end(argp);
}

int main() {
    print_hack (0, &Test::foo);
    print_hack (0, &Test::bar);
    print_hack (0, &Test::bar2);
    print_hack (0, &Test::bar3);
    return 0;
}

Похоже, что значение, хранящееся в указателе (по крайней мере, для GCC), является индексом в виртуальной таблице объектов.
Для невиртуальных функций это похоже на обычный указатель функции.

По сути, вы вынуждены выполнять динамическую диспетчеризацию при использовании функции-указателя-члена, которая содержит виртуальную функцию, по крайней мере, насколько я знаю.

person StoryTeller - Unslander Monica    schedule 29.12.2014

Я согласен с StoryTeller, я не думаю, что это возможно каким-либо стандартным способом. Если вы действительно хотите добиться возможности вызывать либо реализацию базового класса, либо реализацию производного класса с использованием одного и того же указателя на функцию, лучшее, что я могу порекомендовать, это следующее:

struct B {
    virtual int foo() { return fooB(); }
    int fooB() { return 1; }
};

struct D: public B {
    virtual int foo() { return 2; }
};

int main(int argc, char * argv[]) {
    D* pd = new D();
    int (B::*pf)() = &B::fooB;
    int r = (pd->*pf)();
    return 0; 
}

Поскольку тип указателя функции один и тот же, является ли функция виртуальной или нет, то создание невиртуальной функции в базовом классе позволяет напрямую получить ее адрес.

person Vincent Zalzal    schedule 08.04.2015

Я согласен со змеиным доктором, вы должны сделать:

int r = pd->B::foo()

Это просто призыв к методу матери.

person FrenchieTucker    schedule 05.03.2015

Почему бы просто не сделать:

pd->B::foo() 
person snakedoctor    schedule 29.12.2014
comment
Это действительно должен быть комментарий, но кроме того, ОП уже ответил на него. - person StoryTeller - Unslander Monica; 29.12.2014