Home | | Object Oriented Programming | C++ Templates and Classes and its Advantages, disadvantages

Chapter: Object Oriented Programming(OOP) : Advanced Programming

C++ Templates and Classes and its Advantages, disadvantages

Let us say that rather than create a simple templated function, you would like to use templates for a class, so that the class may handle more than one datatype.

Templates and Classes

 

Let us say that rather than create a simple templated function, you would like to use templates for a class, so that the class may handle more than one datatype. You may have noticed that some classes are able to accept a type as a parameter and create variations of an object based on that type (for example the classes of the STL container class hierarchy). This is because they are declared as templates using syntax not unlike the one presented below:

 

template <class T> class Foo

{

public:

Foo();

void some_function();

T some_other_function();

 

private:

int member_variable;

 

T parametrized_variable; };

 

Defining member functions of a template class is somewhat like defining a function template, except for the fact, that you use the scope resolution operator to indicate that this is the template classes' member function. The one important and non-obvious detail is the requirement of using the template operator containing the parametrized type name after the class name.

 

The following example describes the required syntax by defining functions from the example class above.

 

template <class T> Foo<T>::Foo()

{

member_variable = 0;

}

 

template <class T> void Foo<T>::some_function()

{

cout << "member_variable = " << member_variable << endl;

}

 

template <class T> T Foo<T>::some_other_function()

{

return parametrized_variable;

}

 

As you may have noticed, if you want to declare a function that will return an object of the parametrized type, you just have to use the name of that parameter as the function's return type.

 

Advantages and disadvantages

 

Some uses of templates, such as the max() function, were previously filled by function-like preprocessor macros.

 

// a max() macro

#define max(a,b)  ((a) < (b) ? (b) : (a))

 

Both macros and templates are expanded at compile time. Macros are always expanded inline; templates can also be expanded as inline functions when the compiler deems it appropriate. Thus both function-like macros and function templates have no run-time overhead.

 

However, templates are generally considered an improvement over macros for these purposes. Templates are type-safe. Templates avoid some of the common errors found in code that makes heavy use of function-like macros. Perhaps most importantly, templates were designed to be applicable to much larger problems than macros. The definition of a function-like macro must fit on a single logical line of code.

 

There are three primary drawbacks to the use of templates. First, many compilers historically have very poor support for templates, so the use of templates can make code somewhat less portable. Second, almost all compilers produce confusing, unhelpful error messages when errors are detected in template code. This can make templates difficult to develop. Third, each use of a template may cause the compiler to generate extra code (an instantiation of the template), so the indiscriminate use of templates can lead to code bloat, resulting in excessively large executables.

 

The other big disadvantage of templates is that to replace a #define like max which acts identically with dissimilar types or function calls is impossible. Templates have replaced using #defines for complex functions but not for simple stuff like max(a,b). For a full discussion on trying to create a template for the #define max, see the paper "Min, Max and More" that Scott Meyer wrote for C++ Report in January 1995.

 

The biggest advantage of using templates, is that a complex algorithm can have a simple interface that the compiler then uses to choose the correct implementation based on the type of the arguments. For instance, a searching algorithm can take advantage of the properties of the container being searched. This technique is used throughout the C++ standard library.

 

Linkage problems

 

While linking a template-based program consisting over several modules spread over a couple files, it is a frequent and mystifying situation to find that the object code of the modules won't link due to 'unresolved reference to (insert template member function name here) in (...)'.

The offending function's implementation is there, so why is it missing from the object code? Let us stop a moment and consider how can this be possible.

 

Assume you have created a template based class called Foo and put its declaration in the file Util.hpp along with some other regular class called Bar:

 

template <class T> Foo

{

public:

Foo();

T some_function();

T some_other_function();

 

T some_yet_other_function(); T member;

};

 

class Bar

{

Bar();

 

void do_something(); };

 

Now, to adhere to all the rules of the art, you create a file called Util.cc, where you put all the function definitions, template or otherwise:

 

#include "Util.hpp"

 

template <class T> T Foo<T>::some_function()

{

...

}

 

template <class T> T Foo<T>::some_other_function()

{

...

}

 

template <class T> T Foo<T>::some_yet_other_function()

 

{

...

}

 

and, finally:

void Bar::do_something()

{

Foo<int> my_foo;

int x = my_foo.some_function();

int y = my_foo.some_other_function();

}

 

Next, you compile the module, there are no errors, you are happy. But suppose there is an another (main) module in the program, which resides in MyProg.cc:

 

#include "Util.hpp"     // imports our utility classes' declarations, including the template

 

int main()

{

Foo<int> main_foo;

 

int z = main_foo.some_yet_other_function(); return 0;

 

}

 

This also compiles clean to the object code. Yet when you try to link the two modules together, you get an error saying there is an undefined reference to Foo<int>::some_yet_other function() in MyProg.cc. You defined the template member function correctly, so what is the problem?

 

As you remember, templates are instantiated at compile-time. This helps avoid code bloat, which would be the result of generating all the template class and function variants for all possible types as its parameters. So, when the compiler processed the Util.cc code, it saw that the only variant of the Foo class was Foo<int>, and the only needed functions were:

 

int Foo<int>::some_function();

int Foo<int>::some_other_function();

 

No code in Util.cc required any other variants of Foo or its methods to exist, so the compiler generated no code other than that. There is no implementation of some_yet_other_function() in the object code, just as there is no implementation for

 

double Foo<double>::some_function(); or

 

string Foo<string>::some_function();

 

The MyProg.cc code compiled without errors, because the member function of Foo it uses is correctly declared in the Util.hpp header, and it is expected that it will be available upon linking. But it is not and hence the error, and a lot of nuisance if you are new to templates and start looking for errors in your code, which ironically is perfectly correct.

 

The solution is somewhat compiler dependent. For the GNU compiler, try experimenting with the -frepo flag, and also reading the template-related section of 'info gcc' (node "Template Instantiation": "Where is the Template?") may prove enlightening. In Borland, supposedly, there is a selection in the linker options, which activates 'smart' templates just for this kind of problem.

 

The other thing you may try is called explicit instantiation. What you do is create some dummy code in the module with the templates, which creates all variants of the template class and calls all variants of its member functions, which you know are needed elsewhere. Obviously, this requires you to know a lot about what variants you need throughout your code. In our simple example this would go like this:

 

1. Add the following class declaration to Util.hpp: class Instantiations

 

{

private:

 

void Instantiate(); };

 

2. Add the following member function definition to Util.cc: void Instantiations::Instantiate()

 

{

 

 

Foo<int> my_foo; my_foo.some_yet_other_function();

// other explicit instantiations may follow

 

}

 

we never need to actually instantiate the Instantiations class, or call any of its methods. The fact that they just exist in the code makes the compiler generate all the template variations which are required. Now the object code will link without problems.

 

There is still one, if not elegant, solution. Just move all the template functions' definition code to the Util.hpp header file. This is not pretty, because header files are for declarations, and the implementation is supposed to be defined elsewhere, but it does the trick in this situation. While compiling the MyProg.cc (and any other modules which include Util.hpp) code, the compiler will generate all the template variants which are needed, because the definitions are readily available.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Object Oriented Programming(OOP) : Advanced Programming : C++ Templates and Classes and its Advantages, disadvantages |


Privacy Policy, Terms and Conditions, DMCA Policy and Compliant

Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.