Let's say I have a template:
template <class N, class I>
void add(N* element, std::list<N*> & container, I (N::*f)() const,
std::string successmsg, std::string exceptmsg) {
//...
}
And I want to call it for a list of Base Class pointers to a derivative class.
add(newAirplane, airplanes, &Airplane::getRegistration,
"Added!", "Error: Existent!");
Airplane
inherits from AirplaneType
.
Of course, it doesn't compile, N is first defined as AirplaneType
and then as Airplane
.
I 开发者_StackOverflow中文版added a virtual getRegistration
@ AirplaneType but of course, the compiler gives out a vtable error.
What's the proper way to solve this? AirplaneType
has no registration
attribute and I'm not interested in it having one. I also wanted to avoid virtual getRegistration() const {return "";}
Any suggestions for good practice?
EDIT:
Thanks for answers, but still not working. I think I have found the remaining problem, but not its solution:
void Airline::addAirplane(AirplaneType* airplane) {
add(newAirplane, airplanes, &Airplane::getRegistration,
"Added!", "Error: Existent!");
}
The type of pointer received is AirplaneType
, not Airplane
.
airplanes
is a list of AirplaneType
pointers.
You need another template parameter, because you care about two different classes - the type of the pointer (and hence the member function you're going to call with it), and the type of the container:
#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);
}
In this case my add
function doesn't really use the fact that container
is a list
, so a more sensible version might be:
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)();
}
And then again, you could abstract further:
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);
}
... but that's slightly less convenient for the caller:
#include <functional>
add(&a, ls, std::mem_fun(&Airplane::check));
Any suggestions for good practice?
Don't create containers of raw pointers.
Edit: to get this working with a virtual function, with each of my options:
#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));
}
Output is:
check
check
which shows that the override is correctly called even though the function pointer was taken to AirplaneType::check
, not Airplane::check
.
You need to add an additional template parameter for the common base since C++ does not handle contravariant types. That is, std::list<Airplane*>
is an entirely different type from std::list<AirplaneType*>
, and no implicit conversion can occur from the list of pointers to the most derived to the least derived.. So, effectively your add function would need to become:
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)
精彩评论