Template and class heritance

2019-09-15 07:45发布

问题:

The code below does not produce what I want.

#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

template<typename T> class Parent;
template<typename T> class Child1;
template<typename T> class Child2;

template<typename T> T fcn_default(const Parent<T> &obj, const T &phit){
    return 3.2 + phit;
}
template<typename T> T fcn_mod1   (const Child1<T> &obj, const T &phit){
    return 1.2 + phit;
}
template<typename T> T fcn_mod2   (const Child2<T> &obj, const T &phit){
    return 2.2 + phit;
}

template<typename T> class Parent{
    public:
        Parent() {
            fcn_ptr = &fcn_default;
        }
        T do_something(const T &phit){
            return this->fcn_ptr(*this, phit);
        }
        T (*fcn_ptr)(const Parent &, const T &);

};

template<typename T> class Child1 : public Parent<T>{
    public:
        Child1() {
        }
        T (*fcn_ptr)(const Child1 &, const T &);

};

template<typename T> class Child2 : public Parent<T>{
    public:
        Child2() {
        }
        T (*fcn_ptr)(const Child2 &, const T &);

};

typedef double lrtType;

int main(){
    std::vector< Parent<lrtType> * > objects;

    Child1<lrtType> *test11 = new Child1<lrtType>();
    objects.push_back(test11);

    Child1<lrtType> *test12 = new Child1<lrtType>();
    test12->fcn_ptr = &fcn_mod1;
    objects.push_back(test12);

    Child2<lrtType> *test2 = new Child2<lrtType>();
    test2->fcn_ptr = &fcn_mod2;
    objects.push_back(test2);

for (size_t i = 0; i < objects.size(); ++i) {
        std::cout << objects[i]->do_something(2) << std::endl;
}

    cout << "test" << endl;
}

It produces:

5.2
5.2
5.2
test

And I was expecting:

5.2
3.2
4.2
test

My understanding is that the vector objects is of type Parent and therefore calls its own methods and not the children's ones.

I have nearly cracked it using "Curiously recurring template pattern" but I have a last problem during the initialization of the vector objects.

#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

template<class Child, typename T> class Parent;
template<typename T> class Child1;
template<typename T> class Child2;

template<typename T> T fcn_mod1   (const Child1<T> &obj, const T &phit){
    return 1.2 + phit;
}
template<typename T> T fcn_mod2   (const Child2<T> &obj, const T &phit){
    return 2.2 + phit;
}

template<class Child, typename T> class Parent{
    public:
        Parent() {
            fcn_ptr = &Parent::fcn_default;
        }
        T do_something(const T &phit){
            return static_cast<Child*>(this)->fcn_ptr(static_cast<Child*>(this), phit);
        }
        T (*fcn_ptr)(const Child &, const T &);
    private:
        static T fcn_default(const Child &obj, const T &phit){
            return 3.2 + phit;
        }
};

template<typename T> class Child1 : public Parent<Child1<T>,T>{
    public:
        Child1() {
        }
        T (*fcn_ptr)(const Child1 &, const T &);

};

template<typename T> class Child2 : public Parent<Child2<T>,T>{
    public:
        Child2() {
        }
        T (*fcn_ptr)(const Child2 &, const T &);

};

typedef double lrtType;

int main(){
    std::vector< Parent<lrtType> * > objects;

    Child1<lrtType> *test11 = new Child1<lrtType>();
    objects.push_back(test11);

    Child1<lrtType> *test12 = new Child1<lrtType>();
    test12->fcn_ptr = &fcn_mod1;
    objects.push_back(test12);

    Child2<lrtType> *test2 = new Child2<lrtType>();
    test2->fcn_ptr = &fcn_mod2;
    objects.push_back(test2);

for (size_t i = 0; i < objects.size(); ++i) {
        std::cout << objects[i]->do_something(2) << std::endl;
}

    cout << "test" << endl;
}

** +++++ UPDATE +++++ **

If I add a member to a child class, I can not have access to it from within fcn_mod2 for instance. Maybe the virtual functions can help?

#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

template<typename T> class Parent;
template<typename T> class Child1;
template<typename T> class Child2;

template<typename T> T fcn_default(const Parent<T> &obj, const T &phit){
    return 3.2 + phit;
}
template<typename T> T fcn_mod1   (const Parent<T> &obj, const T &phit){
    return 1.2 + phit;
}
template<typename T> T fcn_mod2   (const Parent<T> &obj, const T &phit){
    return 2.2 + phit + param2*0.001;
}

template<typename T> class Parent{
    public:
        Parent() {
            fcn_ptr = &fcn_default;
        }
        T do_something(const T &phit){
            return this->fcn_ptr(*this, phit);
        }
        T (*fcn_ptr)(const Parent &, const T &);

};

template<typename T> class Child1 : public Parent<T>{
    public:
        Child1() {
        }

};

template<typename T> class Child2 : public Parent<T>{
    public:
        Child2() {
        }
        T param2;

};

typedef double lrtType;

int main(){
    std::vector< Parent<lrtType> * > objects;

    Child1<lrtType> *test11 = new Child1<lrtType>();
    objects.push_back(test11);

    Child1<lrtType> *test12 = new Child1<lrtType>();
    test12->fcn_ptr = &fcn_mod1;
    objects.push_back(test12);

    Child2<lrtType> *test2 = new Child2<lrtType>();
    test2->fcn_ptr = &fcn_mod2;
    test2->param2 = 4;
    objects.push_back(test2);

    for (size_t i = 0; i < objects.size(); ++i) {
        std::cout << objects[i]->do_something(2) << std::endl;
    }

    cout << "test" << endl;
}

回答1:

The problem that you have is that your child classes contain an other function pointer fields of the same name as the one in your Parent class. That is usually not what you want, since these classes will have two pointers in them. Why not simply use only one pointer in the parent class. So, you should delete the function pointers in the child classes.

The next problem you have in your second solution is that you try to use runtime virtual dispatch (the vector of Parents) with compile time "virtual" dispatch (the curiously recurring template pattern). This is just not possible. If you want runtime virtual dispatch, then you have to use virtual functions.

If there is no special reason for using function pointers, just use virtual methods only, i.e.:

template<typename T> class Parent{
public:
    virtual T fcn(const Parent<T> &obj, const T &phit){ return 3.2 + phit; }
};

template<typename T> class Child1 : public Parent<T>{
public:
    T fcn(const Parent<T> &obj, const T &phit) override { return 2.2 + phit; }
};

template<typename T> class Child2 : public Parent<T>{
public:
    T fcn(const Parent<T> &obj, const T &phit) override { return 1.2 + phit; }
};

That should work in your scenario. If this is not what you want, please clarify your question.