2016-10-07 8 views
1

Это связано с моей нитью Member Указатели функций и наследование на allegro.cchere.Как мне вызвать метод базового класса с помощью указателя на функцию-член?

См. Тему на allegro.cc для примеров кода.

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

Итак, мой вопрос в том, какие альтернативы существуют? Lambdas были предложены, но я не уверен, как это реализовать, поскольку у меня мало опыта работы с 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++.

+0

Вы говорите: «Должен быть чистый точный способ сделать это», и вы ошибаетесь. По сути, вы пытаетесь обойти предполагаемую работу языковой функции (что вызов через указатель на функцию-член вызывает самую производную версию, если она виртуальна), в функции, которая не была предоставлена ​​с информацией, необходимой для делайте то, что вы намерены. – Peter

+0

@Peter - это четкий контроль над C++, если нет способа вызвать определенную функцию-член через указатель функции-члена. В большинстве случаев вызов производной функции был бы правильным, но в этом случае это не так, и если C++ не предусматривает этого, то он не соответствует imho. У меня есть вера, есть чистый лаконичный способ сделать это, я просто не знаю, что это такое. Он может не включать указатели на функцию-член, но я никогда не говорил, что это должно быть сделано именно так. Кто-то предложил лямбда-функции, но у меня нет достаточного опыта, чтобы они знали, как это сделать. Отсюда призыв к помощи. – MarcD

+0

Извините, MarcD, не так. Большая часть точек виртуальных функций - это то, что они разрешают версию функции, соответствующую объекту ACTUAL, а не унаследованную версию. Это фундаментальная основа полиморфизма. Это ваше требование является ошибочным. – Peter

ответ

1

Простейшим решением является небольшой рефакторинг вашего базового класса. Вместо того, чтобы:

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() и вызвать его непосредственно без особых трудностей.

+0

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

+0

Нечего переопределять. Это не что иное, как изменение имени существующего метода и удаление его «виртуального» ключевого слова, а затем добавление четырех строк кода. Вот и все. –

+0

Неправда. Мне пришлось бы переименовать все существующие виртуальные функции или переопределить их, чтобы вызвать вспомогательную функцию базового класса. Я мог бы оставить функции как есть и удалить виртуальные, но тогда мне все равно придется менять реализации виджета, чтобы использовать новые «базовые» реализации. Существует целая библиотека виджетов, которые используют эти функции. Это не так просто, как добавление четырех строк кода. – MarcD

1

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

Насколько я знаю, вы не можете этого сделать.
Указатель не несет с собой достаточной информации.
Вызов функции-члена с помощью указателя на функцию-члена - это как прямой вызов.
Я подозреваю, что не существует сопоставления с такими вещами, как this->B::f() (обратите внимание, что (this->B::*ptr)() не является допустимым синтаксисом).

Итак, мой вопрос в том, какие альтернативы существуют? Lambdas были предложены, но я не знаю, как это реализовать.

Конечно, вы можете легко обойти его с помощью лямбда-функции.
вытекает минимальный, рабочий пример:

#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.
Я предлагаю прочитать статью Мейерса this, когда он также предложил условия универсальные ссылки для того же.
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).

Это все.

+0

Не могли бы вы объяснить, что это делает? Я не очень хорошо знаком с lambas, или std :: forward. – MarcD

+0

@MarcD Хорошо, позвольте мне добавить более подробную информацию. – skypjack

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^