Как ссылаться на производный класс в элементе шаблона? - PullRequest
0 голосов
/ 26 июня 2018

Код ниже не скомпилируется:

struct Base
{
    std::vector<void(Base::*)(void)> x;
};

struct Derived : public Base
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back(&Derived::foo);

Можно ли ссылаться на производный класс в элементе шаблона x? В приведенном выше примере я точно указываю Base, и производные классы не могут помещать свои собственные функции-члены в вектор x.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Приведение неверно, поскольку ваш код должен предполагать, что он будет вызван только для экземпляра класса Derived. Это означает, что вы должны либо предположить, что все элементы в x являются экземплярами Derived (в этом случае объявление x является общим и должно быть изменено на std::vector<void(Derived::*)(void)> x;), либо вам необходимо сохранить дополнительную информацию, что какой метод класса хранится в определенной позиции x. Оба подхода плохие.

В современном C ++ гораздо лучше сделать это так:

struct Base
{
    std::vector<std::function<void()>> x;
};

struct Derived : public Base
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back([&d](){ d.foo(); });

Другим хорошим подходом может быть CRTP:

template<class T>
struct Base
{
    std::vector<void(T::*)(void)> x;
};

struct Derived : public Base<Derived>
{
    void foo() {}
};

// ...

Derived d;
d.x.push_back(&Derived::foo);
0 голосов
/ 26 июня 2018

Думаю, я бы выразил это, вызвав не виртуальную функцию-член на базе.

пример:

#include <vector>

struct Base
{
    std::vector<void(Base::*)(void)> x;

    // public non-virtual interface    
    void perform_foo()
    {
        foo();
    }

private:
    // private virtual interface for the implementation
    virtual void foo() = 0;    
};

struct Derived : public Base
{
private:
    // override private virtual interface
    void foo() override {}

};

// ...

int main()
{
    Derived d;
    d.x.push_back(&Base::perform_foo);

    auto call_them = [](Base& b)
    {
        for (auto&& item : b.x)
        {
            (b.*item)();
        }
    };

    call_them(d);
}
0 голосов
/ 26 июня 2018

Вы можете, но нет неявного преобразования; это требует броска.

Derived d;
d.x.push_back(static_cast<void(Base::*)()>(&Derived::foo));

Предупреждение: если вы используете этот указатель на член с объектом, который на самом деле не является Derived, поведение не определено. Ступай осторожно.


В качестве дополнения, если вы хотите избавиться от приведения при взятии указателя, вы можете сделать это путем инкапсуляции push (с некоторой статической проверкой типа для загрузки):

struct Base
{
    std::vector<void(Base::*)(void)> x;

    template<class D>
    auto push_member(void (D::* p)()) -> 
    std::enable_if_t<std::is_base_of<Base, D>::value> {
        x.push_back(static_cast<void(Base::*)()>(p));
    }
};
...