Вам нужен другой параметр шаблона, потому что вы заботитесь о двух разных классах - типе указателя (и, следовательно, функции-члена, которую вы собираетесь вызывать с ним), и тип контейнера:
#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
.