Get in touch
Thank you
We will get back to you as soon as possible
.pdf, .docx, .odt, .rtf, .txt, .pptx (max size 5 MB)

12.8.2014

6 min read

7. Template as double-edged sword: go further with OOP!

Introduction In the previous article I began to talk about templates. In this topic I'm going to continue with interacting templates in object oriented design and I want to talk about typename keyword. Less introduction words - let's see what I am talking about. Templates and class features or level up your project with terrible things In previous article I declared class SimpleTemplateClass. I propose to inherit from this class a new one and add new features of templates. There is a big chuck of code which I will describe below:

#include "SimpleTemplateClass.h"
template<typename DataType=int>                                     (1.1)
class InheritTemplateClass : public SimpleTemplateClass<DataType>   (1.2)
{
 public:
  InheritTemplateClass() :
  SimpleTemplateClass<DataType>()                                   (1.3)
  {
  }
  template <typename RValueType>                                    (1.4)
  void operator =(const SimpleTemplateClass<RValueType> &rValue)    (1.5)
  {
    this->size = rValue.Count();
    this->array.reset(new DataType[this->size]);
    for (size_t i = 0; i < rValue.Count(); ++i)
    {
      this->array[i] = static_cast<DataType>(rValue.GetAt(i));      (1.6)
    }
  }
};

First of all look at (1.1) line. There is a new feature of template argument list:

typename DataType=int

It is called default templates argument. If we don't set this argument then it will be int by default:

InheritTemplateClass<> arrayKeeperWithEq;

In such declaration variable's DataType will be taken as int type by default. You can use such feature only in the trailing template arguments. It means you can't write such code:

template<typename A=int, typename B>

It is wrong and will cause compile error. It will compile when you define default argument for B or move template argument A to the end of the list. In line (1.2) we inherit our new class InheritTemplateClass from SimpleTemplateClass. As you see code set template argument DataType for inherited template class. It is requirement of template inheritance to determine template argument list of parent class. In line (1.3) we call appropriate constructor of template parent class with determined template argument list. Lines (1.4) and (1.5) are very interesting. I want to show you such an example code:

SimpleTemplateClass<double> arrayKeeperDouble;                    (2.1)
SimpleTemplateClass<int> arrayKeeperInt;                          (2.2)
// Compile error: no match for ‘operator=’ (operand types are
// ‘SimpleTemplateClass<int>’ and ‘SimpleTemplateClass<double>’)
arrayKeeperInt = arrayKeeperDouble;                               (2.3)
InheritTemplateClass<> arrayKeeperWithEq;
// But it works.
arrayKeeperWithEq = arrayKeeperDouble;                            (2.4)

In lines (2.1) and (2.2) we have made instances of template classes with different types. Line (2.3) causes compile error because arrayKeeperDouble and arrayKeeperInt are different auto-generated types. If they are generated from one template, it doesn't mean that they are same and compiler doesn't know how to assign them. Overloaded assignment operator of InheritTemplateClass in lines (1.4) and (1.5) helps to deal with this. And yes, it is template method of template cs! It defines new template with new template argument RValueType in line (1.4) which is used as type for template argument in other SimpleTemplateClass instance. In body of such operator= we make new internal array and through static_cast() in line (1.6) assign to new array values from array of assigned right hand side instance. static_cast() provide converting from one type to another which helps a lot in line (2.4).   Templates and virtual methods or it is not always what you expect   In the previous section I have told about inheritance which is one of OOP pillars. Let's observe virtual methods which provide polymorphism in C++. There is a pitfall with template methods. Let's see such huge chuck of code:

class BaseClassA                                                          (3.1)
{
public:
virtual void VirtualMethod(int input)                                     (3.2)
{
std::cout << "BaseClassA::VirtualMethod(" << input << ")" << std::endl;
}
};
class ClassB : public BaseClassA                                          (3.3)
{
public:
// It doesn't overload BaseClassA::VirtualMethod(int).
// Adding virtual key word for method causes compile error:
// templates may not be ‘virtual’
template<typename TData>
void VirtualMethod(TData input)                                           (3.4)
{class BaseClassA                                                          (3.1)
std::cout << "ClassB::VirtualMethod("
<< static_cast<int>(input)
<< ")"
<< std::endl;
}
};
template<typename TData>
class ClassC : public BaseClassA                                          (3.5)
{
public:
void VirtualMethod(TData input)                                           (3.6)
{
std::cout << "ClassC::VirtualMethod("
<< static_cast<int>(input)
<< ")"
<< std::endl;
}
};

Let's observe everything in its order: (3.1) is base class which has virtual method (3.2). (3.3) is inherited class from base class and it has template method (3.4) with the same name as virtual method of parent class. If you add virtual key word for (3.4) it will causes compile error, because actually it can't overload virtual method even if it has same auto-generated signature. But some programmers can expect overloading because even without virtual key word it must work. (3.5) is template class which inherited by BaseClassA and it also has method VirtualMethod as parent class. Let's check how everything works by running such code:

std::unique_ptr<BaseClassA> a(new ClassB());
// It prints: BaseClassA::VirtualMethod(10)
a->VirtualMethod(10);                                                   (4.1)
a.reset(new ClassC<int>());
// It prints: ClassC::VirtualMethod(10)
a->VirtualMethod(10);                                                   (4.2)
a.reset(new ClassC<double>());
// It prints: BaseClassA::VirtualMethod(10)
a->VirtualMethod(10);                                                   (4.3)

  Line (4.1) confirms what's stated above: there is no call of overloaded virtual method of child class because it wasn't exactly overloaded. It calls method of the base class. But line (4.2) shows that for template class which is generated VirtualMethod() with int type it works as expected. It calls overloaded method and even its signature was generated by template. Otherwise it looks as in line (4.3). There was generated VirtualMethod() with double type which doesn't overload virtual method. So this line calls method from the base class. As you see there are issues with overloading template classes and methods so beware: it won't work every time even if you hope so.   Key word typename or let compiler know what you said   typename is more than you think. Before it was used only in template argument list, but it has another useful meaning. It is necessary when you have some internal class in other class and you want to use that type from template argument type. I am sure it sounds horrible and confuses you. So, let's see it in the next code:

class ExternalClass
{
public:
class InternalClass                                                   (5.1)
{
};
InternalClass* GetInternalClassWithValue()                            (5.2)
{
return new InternalClass();
}
};
template<typename TData=ExternalClass >
class ClassUsesTypename                                               (5.3)
{
public:
ClassUsesTypename()
{
TData external;
this->internal = external.GetInternalClassWithValue();
}
~ClassUsesTypename()
{
delete internal;
}
private:
// Without typename it causes compile error:
// need ‘typename’ before ‘TData::InternalClass’ because ‘TData’ is a dependent scope
typename TData::InternalClass *internal;                                (5.4)
};

In line (5.1) we have internal class of ExternalClass which is created by method in line (5.2). Declared class in line (5.3) uses InternalClass as type for its field in line (5.4), but we need to get this type through ExternalClass, which is default template type of ClassUsesTypename class. Compiler has lack of information here, because expression TData::InternalClass looks for its like many other things: is it static field or obtaining address of method? It guesses that it is type, but maybe it is programmer typo or mistake so compiler shows error here without required typename keyword in line (5.4). This is for what typename stands for. If you have experience with iterators from C++ standard library, you probably faced with this keyword before. Now you know why it is required, because iterators are internal classes of containers.   Conclusion   Today I have discussed interacting templates in object oriented designed solutions. Article shows different issues with inheritance of classes and overloading of virtual methods. Besides this there were extra features of templates such as: template methods, default template argument type. As separated entity was shown typename keyword which is useful to give the cue for compiler when we use internal class of templated type. For the next article about templates I have left such questions as: specification of templates, duck typing and calculation at compile time. So see you soon. My previous articles from “.CPP files” series are:

0 Comments
name *
email *