简体   繁体   English

成员 function 指针可以引用几个不同类之一的方法吗?

[英]Can a member function pointer refer to a method of one of several different classes?

This is what I have:这就是我所拥有的:

class Foo;
class Bar;
class FooBar 
{
public:
        FooBar( Foo &f, void ( Foo::* signal )( FooBar* ) ) 
                : m_Foo(&f)
                , fp_Foo(signal) {};
        FooBar( Bar &b, void ( Bar::* signal )( FooBar* ) ) 
                : m_Bar(&b)
                , fp_Bar(signal) {};

protected:
        void ( Foo::*fp_Foo )( FooBar* );
        void ( Bar::*fp_Bar )( FooBar* );

private: 
        Foo *m_Foo{nullptr};
        Bar *m_Bar{nullptr};
};

This is what I want:这就是我要的:

class Foo;
class Bar;
class FooBar 
{
public:
        FooBar( Foo &f, void ( Foo::* signal )( FooBar* ) ) 
                : m_Foo(&f)
                , fp(signal) {};
        FooBar( Bar &b, void ( Bar::* signal )( FooBar* ) ) 
                : m_Bar(&b)
                , fp(signal) {};

protected:
        void ( <???>::*fp )( FooBar* ); // Deduplicated

private: 
        Foo *m_Foo{nullptr};
        Bar *m_Bar{nullptr};
};

I want only one set of member function pointers, that can stand in for both Foo and Bar .我只想要一组成员 function 指针,它们可以代表FooBar

Is this possible without the use of a template?如果不使用模板,这可能吗?

Thanks.谢谢。

To me it seems, that you have a XY problem.在我看来,你有一个 XY 问题。 That is why I will concentrate on what you actually want to achieve, ie:这就是为什么我将专注于您真正想要实现的目标,即:

I want to add some signals to a QList based class for insertions, changes, and removals.我想向基于 QList 的类添加一些信号以进行插入、更改和删除。 The Q_OBJECT macro however will not work on this:但是 Q_OBJECT 宏不适用于此:

class CardList : public QList<Card*>

Cause原因

QList is not a QObject . QList不是QObject

Solution解决方案

Subclass QObject and compose the subclass with a list.子类QObject并用列表组合子类。

Example例子

Here is a simple example I have written for you to demonstrate the proposed solution:这是我为您编写的一个简单示例,用于演示建议的解决方案:

#include <QObject>

class Card;

class CardList : public QObject // inheritance
{
    Q_OBJECT
public:
    explicit CardList(QObject *parent = nullptr);

    void addCard(Card *); // emit cardAdded here
    int cardCount() const;
    Card *card(int pos);
    bool removeCard(int pos); // emit cardRemoved here

private:
    QList<Card *> m_cards; // composition

signals:
    void cardAdded(int pos);
    void cardRemoved(int pos);
};

Now you can have:现在你可以拥有:

class Game : public QObject
{
    ...
private:
    CardList *m_cards;
    ...
}

class Player : public QObject
{
    ...
private:
    CardList *m_cards;
    ...
}

That all being said, maybe a model would suite the needs of your application better.话虽如此,也许模型会更好地满足您的应用程序的需求。

One option is to use a std::function in which you store a lambda that calls what you want to call:一种选择是使用std::function存储 lambda 调用你想要调用的内容:

#include <functional>

class FooBar {
public:
    FooBar(Foo& f, void (Foo::*signal)(FooBar*)) : m_f{[&]{ (f.*signal)(this); }}
    {};
    FooBar(Bar& b, void (Bar::*signal)(FooBar*)) : m_f{[&]{ (b.*signal)(this); }}
    {};

    void call() {
        m_f();
    }

private:
    std::function<void()> m_f;
};

Demo演示

If you prefer to have the instance and member function pointer available, you can use a union to accomplish this.如果您希望实例和成员 function 指针可用,则可以使用union来完成此操作。 Since you are using C++17, you should however prefer using a std::variant .由于您使用的是 C++17,因此您应该更喜欢使用std::variant

Here's an example of how that could look:这是一个看起来如何的示例:

#include <utility>
#include <variant>

class FooBar {
public:
    FooBar(Foo& f, void (Foo::*signal)(FooBar*)) : m_fp{std::pair{&f, signal}} {};
    FooBar(Bar& b, void (Bar::*signal)(FooBar*)) : m_fp{std::pair{&b, signal}} {};

    void call() {
        std::visit([this](auto&& fb){
            auto&[i, m] = fb;
            (i->*m)(this);
        }, m_fp);
    }

private:
    // store both the instance pointer and member pointer in pairs:
    std::variant<std::pair<Foo*, void(Foo::*)(FooBar*)>,
                 std::pair<Bar*, void(Bar::*)(FooBar*)>> m_fp;
};

Demo演示

If you for some reason do not want to use a std::variant and std::pair s, just create a similar union :如果您出于某种原因不想使用std::variantstd::pair ,只需创建一个类似的union

struct foopair {
    Foo* i;
    void (Foo::*m)(FooBar*);
};

struct barpair {
    Bar* i;
    void (Bar::*m)(FooBar*);
};

union foobaru {
    foobaru(Foo& f, void (Foo::*signal)(FooBar*)) : fp{&f, signal} {};
    foobaru(Bar& b, void (Bar::*signal)(FooBar*)) : bp{&b, signal} {};    
    foopair fp;
    barpair bp;
};

class FooBar {
public:
    FooBar(Foo& f, void (Foo::*signal)(FooBar*)) :
        has_foo{true}, m_fb{f, signal} {};
    FooBar(Bar& b, void (Bar::*signal)(FooBar*)) :
        has_foo{false}, m_fb{b, signal} {};

    void call() {
        if(has_foo) {
            auto&[i, m] = m_fb.fp;
            (i->*m)(this);
        } else {
            auto&[i, m] = m_fb.bp;
            (i->*m)(this);
        }
    }

private:
    bool has_foo;
    foobaru m_fb;
};

Demo演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM