Указатель на базовую виртуальную функцию-член

Есть ли способ сделать указатель на функцию-член в базовом классе, если это виртуальная функция, и она переопределена в производном классе?

Рассмотрим код следующим образом

#include <iostream>
#include <functional>

struct Base
{
    virtual void g() const
    {
        std::cout << "Base" << std::endl;
    }
};

struct Derived : Base
{
    virtual void g() const override
    {
        std::cout << "Derived" << std::endl;
    }
};

int main()
{
    Derived d;
    (d.*(&Base::g))();
    std::mem_fn(&Base::g)(d);
    return 0;
}

Он печатает «Derived» дважды, несмотря на то, что я делаю указатель на Base::g. Есть ли способ сохранить функцию g виртуальной и переопределенной и получить указатель функции-члена, который будет печатать «Base» для d?


person Fedor    schedule 30.04.2021    source источник


Ответы (2)


Просто вызовите базовую функцию

int main()
{
    Derived d;
    d.g();
    
    Base* b = &d;
    b->Base::g();
    
    //or 
    d.Base::g();

    //or

    auto callLater = [&d]() { d.Base::g();};
    callLater();
    return 0;
}

Выход

Derived
Base
Base
Base

Это можно сделать указателем на функцию; но это должна быть функция-член Base::g() объекта, на который указывает.

person thelizardking34    schedule 30.04.2021

Вы можете определить не виртуальную функцию real_g, вызываемую из g, поэтому код

struct Base
{
  void real_g() const {
    std::cout << "Base" << std::endl;
  }
  virtual void g() const { real_g(); };
};

Затем позже в main

std::mem_fn(&Base::real_g)(d);

См. вики-страницу в таблице виртуальных методов, этот справочник по C++ и стандарт C++ n3337 или выше. Прочтите также хорошую книгу по программированию на C++ и документацию вашего компилятора C++, например. GCC

См. также этот ответ (наивно объясняя, что такое vtables, в простых случаях)

person Basile Starynkevitch    schedule 30.04.2021
comment
Похоже на обман :) - person SergeyA; 30.04.2021
comment
Нет; и хороший оптимизирующий компилятор в любом случае генерирует эквивалентный код - person Basile Starynkevitch; 30.04.2021
comment
Тем не менее, это так. Самая большая проблема здесь заключается в том, что для того, чтобы класс работал так, как этого требует OP, требуются модификации класса. Во-первых, это может быть вообще невыполнимо (класс может быть взят из сторонней библиотеки), во-вторых, вызывает расширение функций-членов. Я не утверждаю, что знаю, как добиться того, чего хочет ОП (иначе я бы опубликовал ответ), но этот ответ мне тоже не нравится. - person SergeyA; 30.04.2021
comment
На самом деле это не решает вопрос о том, есть ли способ сохранить функцию g виртуальной и переопределенной и получить указатель функции-члена, который будет печатать «База d» - person thelizardking34; 30.04.2021
comment
@thelizardking34 технически это так - g все еще переопределен и виртуален - но да, это похоже на обман в технических аспектах. - person SergeyA; 30.04.2021
comment
@SergeyA Если вы собираетесь добавить дополнительные функции; почему бы просто не обернуть его в лямбду вместо того, чтобы менять функциональность класса; и в зависимости от компилятора сделать лучше? - person thelizardking34; 30.04.2021
comment
@BasileStarynkevitch Можем ли мы сделать std::mem_fn(&Base::g)(static_cast‹Base›(d))? - person Jimmy Loyola; 30.04.2021
comment
@JimmyLoyola, тем самым вы создадите копию d, чего не ожидали: создайте указатель/вызываемую функцию для Base::g существующего объекта d. - person Fedor; 15.05.2021