class template question?

I am reading a book and it gives an example of a class template, but doesnt talk it through, so hoping someone can clarify something. The place i am confused in the main function specifically the line AnimalTemplate<Dog> dogTemplate(dog); is dogTemplate the name of the instance of that class, and then (dog) is just passed in as the name.

The way I am thinking this is working is, AnimalTemplate<Dog> is declaring an instance of the class of type dog. dogTemplate is the name of the instance, and (dog) is what needs to be passed in ? Is this right?

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
100
101
102
103
  // Class_Templates.cbp
#include <iostream>

using namespace std;

class Animal
{
protected:
    string m_name;

public:
    Animal(string name) : m_name(name)
    {

    }

    // The interface that has to be implemented
    // in derived class
    virtual string MakeSound() = 0;

    string GetName()
    {
        return m_name;
    }

};

class Dog : public Animal
{
public:
    // Forward the constructor arguments
    Dog(string name) : Animal(name) {}

    // Copy assignment operator overloading
    void operator = (const Dog& D)
    {
        m_name = D.m_name;
    }

    // here we implement the interface
    string MakeSound() override
    {
        return "woof-woof!";
    }

};

class Cat : public Animal
{
public:
    // Forward the constructor arguments
    Cat(string name) : Animal(name) {}

    // Copy assignment operator overloading
    void operator = (const Cat& D)
    {
        m_name = D.m_name;
    }

    // here we implement the interface
    string MakeSound() override
    {
        return "meow-meow!";
    }

};

template<typename T>
void GetNameAndMakeSound(T& theAnimal)
{
    cout << theAnimal.GetName() << " goes ";
    cout << theAnimal.MakeSound() << endl;
}

template <typename T>
class AnimalTemplate
{
private:
    T m_animal;

public:
    AnimalTemplate(T animal) : m_animal(animal) {}

    void GetNameAndMakeSound()
    {
        cout << m_animal.GetName() << " goes ";
        cout << m_animal.MakeSound() << endl;
    }
};

int main()
{
    Dog dog = Dog("Bulldog");
    AnimalTemplate<Dog> dogTemplate(dog);           
    dogTemplate.GetNameAndMakeSound();

    Cat cat = Cat("Persian Cat");
    AnimalTemplate<Cat> catTemplate(cat);
    catTemplate.GetNameAndMakeSound();

    return 0;
}
> is dogTemplate the name of the instance of that class

Yes, dogTemplate is the name of the variable, representing an object (an instance) of the class AnimalTemplate<Dog>


> and then (dog) is just passed in as the name.

The constructor of the class AnimalTemplate<T> ie. AnimalTemplate(T animal) takes an object of type T,
so AnimalTemplate<Dog> takes an object of type Dog; dog is passed as that object.
dogTemplate and catTemplate are some pretty contrived examples.

GetNameAndMakeSound() could just as well been a function in your Animal base class and you would not have needed the templates at all. I don't see that the templates are providing any value in this example.

Templates are a powerful mechanism that lets you write generic code where the type is not known until the template is instantiated.
If that's the actual example of the book, I don't think it's a good book.

> string GetName()
no const-correct, can't be called on const object

> virtual string MakeSound() = 0;
has a virtual member function, but no virtual destructor

> void operator = (const Dog& D)
¿what for? it was already defined (implicit) and now doesn't have return value

> void GetNameAndMakeSound(T& theAnimal)
function in line 69, never used
note that it ask for a reference but shouldn't modify the parameter (see also: const-correctness)

> T m_animal;
> AnimalTemplate(T animal) : m_animal(animal) {}
member of AnimalTemplate, it's a copy of the object passed to the constructor
¿why a copy?

> Dog dog = Dog("Bulldog");
c++ it's not so stupid that you need to repeat every thing that you need (unlike java)
although it is a valid initialization, these are preferred
1
2
3
4
Dog dog("Bulldog");
Dog dog{"Bulldog"};
auto dog = Dog("Bulldog");
auto dog = Dog{"Bulldog"};
Also, remember, polymorphism only works thru indirection (pointer or reference). A direct call to an object's method is exactly that.
Based upon original code, consider:

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
#include <iostream>
#include <vector>
#include <string>

class Animal {
protected:
	std::string m_name;

public:
	Animal(const std::string& name) : m_name(name) { }

	virtual ~Animal() {}
	virtual std::string MakeSound() const = 0;
	std::string GetName() const { return m_name; }
};

class Dog : public Animal {
public:
	Dog(const std::string& name) : Animal(name) {}

	std::string MakeSound() const override { return "woof-woof!"; }
};

class Cat : public Animal {
public:
	Cat(const std::string& name) : Animal(name) {}

	std::string MakeSound() const override { return "meow-meow!"; }
};

template<typename T>
void GetNameAndMakeSound(const T& theAnimal)
{
	std::cout << theAnimal.GetName() << " goes ";
	std::cout << theAnimal.MakeSound() << '\n';
}

template <typename T>
class AnimalTemplate {
private:
	T m_animal;

public:
	AnimalTemplate(const T& animal) : m_animal(animal) {}

	void GetNameAndMakeSound() const {
		std::cout << m_animal.GetName() << " goes ";
		std::cout << m_animal.MakeSound() << '\n';
	}
};

int main()
{
	std::vector<const Animal*> animals;

	const Dog dog {"Bulldog"};
	GetNameAndMakeSound(dog);

	const AnimalTemplate<Dog> dogTemplate(dog);
	dogTemplate.GetNameAndMakeSound();

	animals.push_back(&dog);

	const Cat cat {"Persian Cat"};
	GetNameAndMakeSound(cat);

	const AnimalTemplate<Cat> catTemplate(cat);
	catTemplate.GetNameAndMakeSound();

	animals.push_back(&cat);

	for (const auto& a : animals)
		std::cout << a->GetName() << " goes " << a->MakeSound() << '\n';
}


Note the use of the vector to hold pointers to different derived types. This is a simple example of polymorphism.


Bulldog goes woof-woof!
Bulldog goes woof-woof!
Persian Cat goes meow-meow!
Persian Cat goes meow-meow!
Bulldog goes woof-woof!
Persian Cat goes meow-meow!

Last edited on
Topic archived. No new replies allowed.