开发者

C++ - want virtual acting solely as redirection

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜