Template with multiple inharitance

Hello, I am really new for C ++ and came hare from Java (it's not me, it's my university program) And I stopped on one task that is really hard to me to implement on C ++
Last edited on
, I already made simple classes and extending , but with templates something goes wrong. Can u show me how to implement this task correctly?
The base class template

template <class T> class Num {};
includes protected the ad's value type t , and in the open part - virtual method Print () output of values ​​on the screen. Define derived classes - Square_Num and Sqrt_Num. In class Square_Num virtual method Print () displays the square value X, and the class Sqrt_Num - square root of x. The main function of a pointer to the base class Num, which consistently addresses initialized objects derived classes created in the dynamic memory. Based on the mechanism of identification types (class typeinfo) determine the class name, which really points to a pointer. Use the link on base class for virtual methods.
Last edited on
There are four possible combinations of templates and inheritance as described here:
http://www.prenhall.com/divisions/esm/app/kafura/secure/chapter7/html/7.5_inheritance.htm
Your problem, as you have laid it out, falls in the second category viz. non-template class (since Square_Num and Sqrt_Num

are not described as template classes) inherit from a template class (Num<T>). Below is an example based on your

requirements (I hope):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include <cmath>
#include <memory>
using namespace std;

template <typename T>
class Num
{
    private:
        T m_val;
    public:
        Num (T val) : m_val(val) {}
        virtual void Print()const
        {
            cout << "Num: " << m_val << '\n';
        }
        T getVal()const{return m_val;}
        virtual ~Num(){}
};
template <typename T>
class Square_Num : public Num<T>
{
   public:
        Square_Num (T val) : Num<T> (val){}
        virtual void Print ()const
        {
            cout << "Square_Num " << pow(Num<T>::getVal(), 2) << '\n';
        }
};
template <typename T>
class Sqrt_Num : public Num<T>
{
    public:
        Sqrt_Num (T val) : Num<T> (val){}
        virtual void Print ()const
        {
            cout << "Sqrt_Num " << pow(Num<T>::getVal(), 0.5) << '\n';
        }
};

int main()
{
    unique_ptr<Num<int>> base_ptr_square (new Square_Num<int> (9));
    //Square_Num needs type specifier because using base (template) class pointer
    base_ptr_square -> Print();
    unique_ptr<Num<int>> base_ptr_sqrt (new Sqrt_Num<int> (121));
    base_ptr_sqrt -> Print();
}
OP: in the previous program I'd actually provided the more general solution i.e the derived classes were also template classes. Below the derived classes are not template classes - notice the key differences (a) the base class is specialized in the declaration of the derived classes, not in main() as previous and (b) in main() - the new operator on the derived classes don't require any typename qualifications:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<iostream>
#include <cmath>
#include <memory>
using namespace std;

template <typename T>
class Num
{
    private:
        T m_val;
    public:
        Num (T val) : m_val(val) {}
        virtual void Print()const
        {
            cout << "Num: " << m_val << '\n';
        }
        T getVal()const{return m_val;}
        virtual ~Num(){}
};
class Square_Num : public Num<int>
{
   public:
        Square_Num (int val) : Num<int> (val){}
        virtual void Print ()const
        {
            cout << "Square_Num " << pow(Num<int>::getVal(), 2) << '\n';
        }
};
class Sqrt_Num : public Num<int>
{
    public:
        Sqrt_Num (int val) : Num<int> (val){}
        virtual void Print ()const
        {
            cout << "Sqrt_Num " << pow(Num<int>::getVal(), 0.5) << '\n';
        }
};

int main()
{
    unique_ptr<Num<int>> base_ptr_square (new Square_Num (9));
    //no type specifier needed for derived classes
    base_ptr_square -> Print();
    unique_ptr<Num<int>> base_ptr_sqrt (new Sqrt_Num (121));
    base_ptr_sqrt -> Print();
}
Last edited on
here's an extension to the above using (a) template abstract base class (ABC), (b) template child classes inherited from the template ABC, (c) template grand-child classes inherited from the template child classes and (d) run-time type identification (RTII) using std::conditional:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <memory>
#include <cmath>

template <typename T>
class Num
{
    public:
    virtual ~ Num() = default;
    virtual void print()const = 0;
};

template <typename T>
class PosNum : public Num<T>
{
    T m_num;
public:
    static size_t counter;
    PosNum(T num)
    {
        if(num >= 0)
        {
            m_num = num;
            counter ++;
        }
    }
    T getNum()const{return m_num;}
    virtual void print()const
    {
        std::cout << "PosNum: \t";
    }
};
template <typename T>
size_t PosNum<T>::counter = 0;

template <typename T>
class PosNumSq : public PosNum<T>
{
    public:
        PosNumSq (T num) : PosNum<T> (num){}
        virtual void print ()const
        {
            std::cout << "Square child of PosNum " << pow(PosNum<T>::getNum(), 2) << '\n';
        }
};

template <typename T>
class NegNum : public Num<T>
{
    T m_num;
public:
    static size_t counter;
    NegNum(T num)
    {
        if(num < 0)
        {
            m_num = num;
            counter ++;
        }
    }
    T getNum()const{return m_num;}
    virtual void print()const
    {
        std::cout << "NegNum: \t";
    }
};
template <typename T>
size_t NegNum<T>::counter = 0;
template <typename T>
class NegNumSq : public NegNum<T>
{
    public:
        NegNumSq (T num) : NegNum<T> (num){}
        virtual void print ()const
        {
            std::cout << "Square child of NegNum " << pow(NegNum<T>::getNum(), 2) << '\n';
        }
};

int main()
{
    std::cout << "Enter number: \n";
    int num{};
    std::cin >> num;
    PosNumSq<int> a(num);
    NegNumSq<int> b(num);

    if(PosNum<int>::counter > NegNum<int>::counter)
    {
        std::unique_ptr<Num<int>> base_ptr_square (new std::conditional<true, PosNumSq<int>, NegNumSq<int>>::type (num));
        base_ptr_square -> print();
    }
    else
    {
        std::unique_ptr<Num<int>> base_ptr_square (new std::conditional<false, PosNumSq<int>, NegNumSq<int>>::type (num));
        base_ptr_square -> print();
    }
}

edit: virtual destructor to ABC added
Last edited on
To my understanding, there's no RTTI in the last case.
std::conditional has nothing to do with that -- the meaning of the type std::conditional<A,B,C>::type depends on the value of the non-type template parameter A, which is (always) a constant expression.

1
2
3
4
5
6
...
int main() { 
  std::unique_ptr<Num<int>> base_ptr_square = std::make_unique<PosNumSq<int>>(num);
  auto sq_typeinfo = typeid(*base_ptr_square);
  std::cout << "name: " << sq_typeinfo.name() << "\n";
}


Also you should default virtual destructors in your parent classes, since you're relying on polymorphic destruction.
virtual ~ Num() = default;

Last edited on
Output from two runs of the program:
1
2
3
4
5
6
Enter number:
9
Square child of PosNum 81
Enter number:
-9
Square child of NegNum 81

The if-else on std::conditional linked to the static data members of PosNum, NegNum achieves the RTII in this case

Thanks for the heads-up on the virtual dtor, somehow it slipped out from my earlier posts. I've edited it back in.


Max: on further thought I think there's something to your observation. The program does not RTII the variable num but rather the subsequent variable created by the new operator. Though the two are related, this point should also be taken into a/c. Thanks
The if-else on std::conditional linked to the static data members of PosNum, NegNum achieves the RTII in this case

In C++, "type" is the property of expressions. Expressions have either dynamic or static type. At compile time (when templates are instantiated) only static type is considered.
More generally, template metacode does all it's work at compile time -- "run-time type" can't have anything to do with it.
With enough work you could unfold the metaprogram yourself. This is evidence that dynamic type isn't involved. In this case it's easy -- just replace every occurrence of typename std::conditional<true, PosNumSq<int>, NegNumSq<int>>::type with PosNumSq<int>.

The program does not RTII the variable num but rather the subsequent variable created by the new operator.

I think you're confusing dynamic dispatch (calling the right virtual function implementation) with RTTI.

Dynamic dispatch is accomplished using a virtual table which contains pointers to most-derived functions, but it still doesn't require bringing the concept of "type" into a program at runtime. The RTTI mechanisms do, though. See:
http://en.cppreference.com/w/cpp/language/dynamic_cast
http://en.cppreference.com/w/cpp/language/typeid
Last edited on
Topic archived. No new replies allowed.