Это связано с моей темой Указатели на функции-члены и наследование на allegro.cc Вот.
Смотрите тему на allegro.cc для примеров кода.
Я пытаюсь вызвать метод базового класса через указатель на функцию-член виртуального метода в моем WidgetBase
учебный класс. Однако он вызывает виртуальный метод вместо базового класса. Я хочу вызвать метод базового класса через указатель на функцию, но в соответствии с другими потоками здесь при переполнении стека это невозможно.
Итак, мой вопрос, какие есть альтернативы? Были предложены лямбды, но я не уверен, как это реализовать, поскольку у меня мало опыта работы с C ++ 11 или C ++ 14. Идеи будут приветствоваться.
Можно вызвать метод базового класса, если вызывающий объект на самом деле является экземпляром Base
Класс, но это вызывает проблемы, потому что это включает в себя создание копии объекта, для которого я хочу вызвать метод, что сводит на нет цель вызова его для объекта с самого начала.
Я надеялся, что есть какое-то волшебное приведение, которое я мог бы использовать для реализации этого, но использование статического приведения для приведения к базовому классу делает временную копию объекта, что, опять же, не то, что я хочу.
Я хочу использовать это для реализации вспомогательной функции в моем TextDecorator
класс, который будет вызывать предварительно выбранную функцию установки состояния для нескольких различных объектов, содержащихся в TextDecorator
, TextDecorator
происходит от WidgetDecoratorBase
, который является производным от WidgetBase
, где находятся функции базового класса, которые я хочу вызвать. Затем эта вспомогательная функция будет вызываться в переопределенных версиях функций установщика состояний в TextDecorator
учебный класс.
В основном, то, что я хочу в псевдо-C ++ коде это:
void TextDecorator::SetFlagState(bool state , void (WidgetBase::*StateSetter)(bool)) {
if (basic_text_widget) {
(basic_text_widget->WidgetBase::*StateSetter)(state);
}
(text_widget_layout->WidgetBase::->*StateSetter)(state);
(this->WidgetDecoratorBase::->*StateSetter)(state);
}
Это тогда позволило бы мне сделать мои звонки установки состояния в TextDecorator
ясно и кратко, чтобы называться так:
void TextDecorator::SetEnabledState(bool state) {
SetFlagState(state , SetEnabledState);
}
где SetEnabledState
это одна из функций настройки виртуального состояния в моем WidgetBase
учебный класс.
Пожалуйста, держите предложения ограниченными возможными способами реализации этого. Должен быть чистый лаконичный способ сделать это в C ++.
Самое простое решение — это небольшой рефакторинг вашего базового класса. Вместо:
class Base {
public:
virtual void foo()
{
// The base implementation of foo().
}
};
Заменить его на:
class Base {
public:
virtual void foo()
{
foo_base();
}
void foo_base()
{
// base class implementation of foo.
}
};
Код замены на 100% логически эквивалентен. Но теперь вы можете получить указатель на foo_base()
метод, и вызвать его напрямую без каких-либо особых трудностей.
Я хочу вызвать метод базового класса через указатель на функцию, но в соответствии с другими потоками здесь при переполнении стека это невозможно.
Насколько я знаю, ты не можешь этого сделать.
Указатель не несет с собой достаточно информации.
Вызов функции-члена через указатель на функцию-член похож на прямой вызов.
Я подозреваю, что не существует никакого сопоставления с такими вещами, как this->B::f()
(Обратите внимание, что (this->B::*ptr)()
не является допустимым синтаксисом).
Итак, мой вопрос, какие есть альтернативы? Лямбды были предложены, но я не уверен, как это осуществить
Конечно, вы можете обойти это легко с помощью лямбда-функции.
Следует минимальный рабочий пример:
#include<iostream>
#include<utility>
struct B {
virtual void f() { std::cout << "B" << std::endl; }
};
struct D: B {
void f() override { std::cout << "D" << std::endl; }
template<typename F>
void g(F &&f) { std::forward<F>(f)(*this); }
};
int main() {
D d;
d.g([](auto &i){ i.B::f(); });
}
Вместо того, чтобы передавать указатель на функцию-член, вы передаете лямбду-делегат, которая выполняет всю работу.
Недостатком этого подхода является то, что он не работает «из коробки», если функция виртуального члена является закрытой.
Как и просили в комментариях, я добавляю больше деталей.
Давайте посмотрим на это:
template<typename F>
void g(F &&f) { std::forward<F>(f)(*this); }
В этом случае, g
это шаблон функции, который принимает вызываемый объект. &&
рядом с f
потому что f
является ссылкой для пересылки, и ее можно использовать для привязки либо к ссылке lvalue, либо к ссылке rvalue.
Я бы предложил прочитать этот статья Майерса, когда он также предложил условия универсальная ссылка за то же самое.
std::forward
служит для пересылки переменной, сохраняя в точности ее тип. Обратите внимание, что переменная, имеющая тип Rvalue ссылка на будет ссылкой на lvalue в контексте g
иначе.
В вышеупомянутой статье это объясняется более подробно.
Теперь рассмотрим эту строку кода:
d.g([](auto &i){ i.B::f(); });
Мы призываем g
используя общую лямбду в качестве аргумента.
Это не является строго необходимым, мы могли бы использовать это вместо:
d.g([](D &i){ i.B::f(); });
Помимо того факта, что тип выводится, идея состоит в том, что мы получаем экземпляр класса, который наследуется от B
и мы называем реализацию f
от B
в этом случае.
Кто требует дать такую ссылку?
Вернуться к предыдущему фрагменту:
template<typename F>
void g(F &&f) { std::forward<F>(f)(*this); }
Nite, что здесь мы вызываем f
(это наша лямбда) с помощью *this
в качестве аргумента (он формирует ссылку на объект типа D
).
Это все.