C ++ - хочу, чтобы виртуальная игра действовала исключительно как перенаправление - PullRequest
1 голос
/ 04 ноября 2010

Допустим, у меня есть шаблон:

template <class N, class I>
void add(N* element, std::list<N*> & container, I (N::*f)() const,
        std::string successmsg, std::string exceptmsg) {
//...
}

И я хочу вызвать его для списка указателей базового класса на производный класс.

add(newAirplane, airplanes, &Airplane::getRegistration,
        "Added!", "Error: Existent!");

Airplane наследуетиз AirplaneType.

Конечно, он не компилируется, N сначала определяется как AirplaneType, а затем как Airplane.

Я добавил виртуальный getRegistration @ AirplaneTypeно, конечно, компилятор выдает ошибку vtable.

Как правильно решить эту проблему?AirplaneType не имеет атрибута registration, и я не заинтересован в его наличии.Я также хотел бы избежать virtual getRegistration() const {return "";}

Любые предложения для хорошей практики?

РЕДАКТИРОВАТЬ:

Спасибо за ответы, но все еще не работает.Я думаю, что нашел оставшуюся проблему, но не ее решение:

void Airline::addAirplane(AirplaneType* airplane) {
add(newAirplane, airplanes, &Airplane::getRegistration,
        "Added!", "Error: Existent!");

}

Тип полученного указателя AirplaneType, а не Airplane.

airplanes - списокиз AirplaneType указателей.

Ответы [ 2 ]

2 голосов
/ 04 ноября 2010

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

#include <list>

struct AirplaneType {
};

struct Airplane : AirplaneType {
    int check() const { return 3; }
};

template <typename T, typename U, typename I>
void add(T* element, std::list<U*> & container, I (T::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

int main() {
    std::list<AirplaneType*> ls;
    Airplane a;
    add(&a, ls, &Airplane::check);
}

В этом случае моя функция add на самом деле не использует тот факт, что container является list, поэтому более разумной версией может быть:

template <typename T, typename U, typename I>
void add(T* element, U & container, I (T::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

И снова,Вы могли бы абстрагироваться дальше:

template <typename T, typename U, typename AUF>
void add(T element, U & container, AUF func) {
    container.push_back(element);
    typename AUF::result_type i = func(element);
}

... но это немного менее удобно для звонящего:

#include <functional>

add(&a, ls, std::mem_fun(&Airplane::check));

Есть какие-нибудь предложения для хорошей практики? Не создавать контейнеры с необработанными указателями.

Редактировать: чтобы это работало с виртуальной функцией, с каждым из моих вариантов:

#include <list>
#include <functional>
#include <iostream>

struct AirplaneType {
    virtual int check() const { return 0; }
};

struct Airplane : AirplaneType {
    int check() const { std::cout << "check\n"; return 3; }
};

template <typename T, typename U, typename I>
void add(U* element, std::list<T*> & container, I (U::*f)() const) {
    container.push_back(element);
    I i = (element->*f)();
}

template <typename T, typename U, typename AUF>
void add2(T element, U & container, AUF func) {
    container.push_back(element);
    typename AUF::result_type i = func(element);
}

int main() {
    std::list<AirplaneType*> ls;
    Airplane a;
    add(static_cast<AirplaneType*>(&a), ls, &AirplaneType::check);
    add2(&a, ls, std::mem_fun(&AirplaneType::check));
}

Вывод:

check
check

, который показывает, что корректно вызывается переопределение, даже если указатель функции был взят в AirplaneType::check, а не Airplane::check.

2 голосов
/ 04 ноября 2010

Вам необходимо добавить дополнительный параметр шаблона для общей базы, поскольку C ++ не обрабатывает контравариантные типы. Таким образом, std::list<Airplane*> является совершенно другим типом от std::list<AirplaneType*>, и не может произойти неявное преобразование из списка указателей в наиболее производные в наименее производные. Таким образом, ваша функция добавления должна стать:

template <class N, class I, class B>
void add(N* element, std::list<B*> & container, I (N::*f)() const,
        std::string successmsg, std::string exceptmsg)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...