Templates - PowerPoint PPT Presentation

1 / 38
About This Presentation
Title:

Templates

Description:

Templates – PowerPoint PPT presentation

Number of Views:65
Avg rating:3.0/5.0
Slides: 39
Provided by: hawb3
Category:
Tags: templates

less

Transcript and Presenter's Notes

Title: Templates


1
Templates
  • Consider the following function, which swaps two
    integers
  • void swap(int x, int y)
  • int temp x
  • x y
  • y temp
  • int i 3, j 4
  • swap(i, j)

2
  • void swap(int x, int y)
  • int temp x
  • x y
  • y temp
  • Now suppose we also want a function to swap
    floats. Then we write another function (we can
    overload the name swap for different argument
    types)
  • void swap(float x, float y)
  • float temp x
  • x y
  • y temp
  • float f 4.0, g 5.0
  • swap(f, g)

3
  • But writing the same code over and over for every
    possible type can get tiring fast. What wed
    really like would be a single swap function that
    worked for all types. Lets express what we want
    as follows
  • // For any type T, swap two elements of type T
  • void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • swap is supposed to take an two elements of some
    type (lets call this type T for the moment), and
    swap them. But we dont know what T is when we
    write swap - ideally, swap should work for all
    possible types T.

4
  • C supports template declarations which express
    exactly what we want
  • template void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • This declares a template function swap which can
    swap any type.
  • A template is sort of like a function that takes
    a type as a parameter. The class T declaration
    indicates that T is an argument which can be
    instantiated with any type (despite the name
    class, this type can be any type, not just a
    class type).

5
  • template void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • swap can be instantiated with different types to
    produce swap functions specialized to particular
    types
  • swap is a function that swaps ints.
  • swap is a function that swaps strings.
  • For example, swap substitutes int for T to
    produce a function to swap ints
  • void swap(int x, int y)
  • int temp x
  • x y
  • y temp

6
  • template void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • Heres an example that uses swap to swap a
    couple of integers, and swap to swap a
    couple of floating point numbers
  • int i 3, j 4
  • swap(i, j)
  • float f 4.0, g 5.0
  • swap(f, g)

7
  • template void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • In practice, C can usually infer the right type
    parameter based on the arguments to a template
    function
  • int i 3, j 4
  • swap(i, j) // uses swap
  • float f 4.0, g 5.0
  • swap(f, g) // uses swap

8
  • To see how useful templates can be, recall the
    qsort function in stdlib.h
  • void qsort(void base, int num, size_t width, int
    (compare)(void elem1, void elem2 ) )
  • This was messy to use because of void. For
    instance, our compare function for integers
    looked like
  • int compareInt(void i1Ptr, void i2Ptr)
  • if(((int)i1Ptr) -1
  • else if(((int)i1Ptr) ((int)i2Ptr))
    return 0
  • else return 1

9
  • We can use templates to get rid of the void
  • template void qsort(T base, int num,
    int (compare)(T elem1, T elem2 ) )
  • This declares a template function qsort which can
    sort any type.

10
  • template void qsort(T base, int num,
    int (compare)(T elem1, T elem2 ) )
  • Example (notice that we can write a nice
    compareInt function without a bunch of casts)
  • int compareInt(int i1, int i2)
  • if(i1
  • else if(i1 i2) return 0
  • else return 1
  • int iArr4
  • iArr0 3
  • iArr1 2
  • iArr2 7
  • iArr3 5
  • qsort(iArr, 4, compareInt)

11
  • Notice also that the templates enforce a stronger
    typing property than the void did the type of
    the compare function passed in must match the
    type of the array. So
  • qsort(iArr, 4, compareInt)
  • typechecks, because compareInt and iArr both are
    built out of type int. But
  • string sArr4
  • qsort(sArr, 4, compareInt)
  • wont typecheck, because sArr doesnt match the
    type of the compareInt function.

12
  • Templates can also be used to create generic data
    structures.
  • Consider our old FloatArray class, which only
    worked for elements of type float
  • class FloatArray
  • float arr
  • int arrSize
  • public
  • FloatArray(int size)
  • FloatArray(const FloatArray from)
  • FloatArray operator (const FloatArray
    from)
  • FloatArray()
  • float get(int i) const
  • void set(int i, float f)
  • int size()

13
  • By declaring and implementing the class as a
    template, we can make it work for any type
  • template class TArray
  • T arr
  • int arrSize
  • public
  • TArray(int size)
  • TArray(const TArray from)
  • TArray operator (const TArray
    from)
  • TArray()
  • T get(int i) const
  • void set(int i, T f)
  • int size()
  • For example, TArray is a class for an
    array of floats, and TArray is a class
    for an array of strings.

14
  • template class TArray
  • T arr
  • int arrSize
  • public
  • TArray(int size)
  • TArray(const TArray from)
  • TArray operator (const TArray
    from)
  • TArray()
  • T get(int i) const
  • void set(int i, T f)
  • int size()
  • Example
  • TArray tArr(4)
  • tArr.set(3, 5)
  • int i tArr.get(3)

15
  • template class TArray
  • T arr
  • int arrSize
  • public
  • TArray(int size)
  • TArray(const TArray from)
  • TArray operator (const TArray
    from)
  • TArray()
  • T get(int i) const
  • void set(int i, T f)
  • int size()
  • Implementing member functions of a template
    class
  • template int TArraysize()
  • return arrSize

16
  • Template functions and template classes can be
    used together. For instance, the following
    function sorts a TArray
  • template void arraysort(TArray arr,
  • int (compare)(T elem1, T elem2 ) )
  • Notice again that the element type of the TArray
    must match the type used by the compare function.
  • Example
  • TArray tArr(4)
  • ...
  • arraysort(tArr, compareInt)

17
  • A template can take multiple type arguments
  • template class Pair
  • public
  • T x1
  • U x2
  • Pair(T a1, U a2) x1(a1), x2(a2)
  • Pair p1("deep ", 6)
  • cout

18
The standard template library
  • There are a number of built-in data structures
    that are available for you to use, such as list
    and vector.
  • For instance, list is a list of strings,
    and vector is a vector of pointers to
    Shapes.
  • Example
  • include
  • include
  • using namespace std // necessary on some
    platforms
  • list l1 // list of ints
  • vector v1 // vector of pointers to Shapes

19
  • 2 dimensional arrays can now be implemented as
    vectors of vectors
  • vector arr
  • Pitfall the space in is necessary. If you
    type
  • vector arr
  • then C thinks the means right-shift.

20
Polymorphism strikes again
  • Remember how inheritance and virtual functions
    led to a form of polymorphism? A variable of
    type Shape could hold many different types of
    objects at run-time (Circles, Triangles, etc.).
  • Templates also provide a form of polymorphism.
    Inside a template function or class, a
    variable of type T may refer to many different
    types of objects, depending on what type T is
    instantiated with.

21
  • Lets see how these two forms of polymorphism
    compare.
  • Before templates were introduced into C, many
    vendors provided data structure classes based on
    inheritance. Usually, all elements would have to
    be derived from a common base class called
    Object.
  • Then the data structure classes would work with
    Objects
  • class Array
  • Object arr
  • public
  • ...
  • Object get(int i) const
  • void set(int i, Object f)

22
  • class Array
  • Object arr
  • public
  • ...
  • Object get(int i) const
  • void set(int i, Object f)
  • So if we write a class that inherits from Object,
    we can put objects of this class into an Array
  • class MyString public Object
  • ...
  • Array arr(5)
  • arr.set(0, new MyString(hello))
  • arr.set(1, new MyString(there))

23
  • class Array
  • Object arr
  • public
  • ...
  • Object get(int i) const
  • void set(int i, Object f)
  • But if we try to read a MyString object from the
    array, we find it has type Object instead of
    MyString
  • Object o arr.get(0)
  • So we have to do a cast (yuck!) to make it a
    MyString
  • MyString m (MyString ) o

24
  • With templates, we know that the type we get out
    of a data structure is exactly the type we put
    into the data structure. So no cast is
    necessary
  • template class TArray
  • T arr
  • public
  • ...
  • T get(int i) const
  • void set(int i, T f)
  • Array arr(5)
  • arr.set(0, new MyString(hello))
  • arr.set(1, new MyString(there))
  • MyString m arr.get(0) // no cast necessary
  • So templates are clearly a nicer, safer way to
    implement generic data structure classes.

25
  • On the other hand, inheritance is the best way to
    express a common interface shared by a collection
    of closely related classes. For instance, a
    collection of shapes can rely on a common base
    class Shape
  • class Shape
  • ...
  • public
  • virtual void draw()
  • virtual void rotate(double angle)
  • virtual double area()
  • class Circle public Shape ...
  • class Triangle public Shape ...

26
  • Suppose we didnt use inheritance to implement
    shapes, and instead defined each shape
    independently, without a common base class
  • class XCircle
  • ...
  • void draw()
  • void rotate(double angle)
  • double area()
  • class XTriangle
  • ...
  • void draw()
  • void rotate(double angle)
  • double area()

27
  • Now suppose we wrote a template function
    rotateAndDraw, designed to work for XCircles and
    XTriangles
  • template
  • void rotateAndDraw(T s, double angle)
  • s.rotate(angle)
  • s.draw()
  • The function assumes that T contains rotate and
    draw member functions, but it doesnt document
    this clearly. For instance, it doesnt say what
    argument and return types the rotate and draw
    functions should have.
  • This is bad style.

28
  • It isnt clear from the template declaration
  • template void rotateAndDraw(T s, double
    angle)
  • what types are valid for T. For instance, is an
    int ok?
  • rotateAndDraw(5, 3.0) // ???
  • This in fact does not typecheck, because ints
    dont have rotate and draw functions. However,
    we have to look at rotateAndDraws implementation
    to figure this out.
  • Using inheritance is much better in this case
  • void rotateAndDraw(Shape s, double angle)
  • s.rotate(angle)
  • s.draw()
  • Here, the type of s is clearly stated.

29
  • Inheritance also allows more heterogeneity.
    Suppose we want a single list that holds all
    different kinds of shapes.
  • If shapes arent related by inheritance, we can
    create lists of specific types
  • list l1
  • list l2
  • But l1 can only hold XCircles, and l2 can only
    hold XTriangles. Neither list can hold a mixture
    of the two. If Circle and Triangle share the
    base class Shape, though, then
  • list l3
  • can hold all different kinds of shapes, including
    Circles and Triangles.

30
  • Notice that
  • list l3
  • takes advantage both of templates and
    inheritance.
  • This combination of templates and inheritance is
    quite common and very useful. It uses the
    strengths of templates and inheritance in a
    complementary way.

31
Templates the good, the bad, the ugly
  • good
  • templates are excellent at expressing generic
    algorithms and data structures
  • bad and ugly
  • template implementations go into header files
    instead of .cpp files
  • template instantiations create multiple copies of
    a templates code
  • template uses cant be typechecked without
    expanding the entire template

32
  • Problem 1 On most platforms, the entire
    implementation of a template must go in the
    header file
  • // swap.h
  • template void swap(T x, T y)
  • T temp x
  • x y
  • y temp
  • // .cpp file
  • include swap.h
  • ...
  • This violates everything weve told you about
    source code organization. Usually only
    declarations go in the header file, and
    implementations go in a .cpp file.

33
  • Problem 2 A different copy of a templates code
    is produced for every different type passed in as
    the templates type parameter.
  • template void qsort(T base, int num,
    int (compare)(T elem1, T elem2 ) )
  • For instance, if the qsort template above is
    instantiated for ints, floats, and strings, then
    3 different copies of qsorts code are generated
  • qsort(...) // generates code for qsort
  • qsort(...) // generates code for
    qsort
  • qsort(...) // generates code for
    qsort

34
  • If the templates are complicated, then this code
    bloat can get quickly out of hand. C
    programmers sometimes complain that their
    executable files are many megabytes in size
    because of template instantiation.
  • By contrast, the old qsort in stdlib.h only
    required one copy of the code, which worked for
    all types (through the admittedly clunky void
    mechanism).

35
  • Problem 3 typechecking template instantiations
  • template
  • void rotateAndDraw(T s, double angle)
  • Weve already seen the rotateAndDraw example that
    expects its type T to have rotate and draw member
    functions. This means that trying to use
  • rotateAndDraw(5, 3.0)
  • wont typecheck. But how do we know that it
    wont typecheck? We have to look through
    rotateAndDraws implementation, to see what
    things it does with the objects of type T.
  • This violates the idea of separating the
    implementation of a function from its interface.

36
  • A practical consequence of this is that C needs
    to expand out uses of a template entirely before
    it can typecheck them.
  • If the template is complicated, This leads to
    remarkably obscure type errors when something
    goes wrong. Heres a small (incorrect) example
    that uses the standard library class map
  • map m1 // maps strings to ints
  • // incorrectly tries to insert a pair (hello,
    6)
  • // into the map
  • m1.insert(m1.begin(), make_pair("hello", 6))
  • This incorrectly passes in a char argument
    (hello) to make_pair instead of a string.
    Lets see what type error we get.

37
  • map m1 // maps strings to ints
  • m1.insert(m1.begin(), make_pair("hello", 6))
  • Visual Studio 5.0 reports the following type
    error
  • error C2664 'class std_Treestdbasic_stringr,class stdallocator,struct
    stdpairstdchar_traits, class stdallocator
    ,int,struct stdmapr,struct stdchar_traits,class
    stdallocator,int,struct stdlessstdbasic_stringr,class stdallocator,class
    stdallocator_Kfn,struct stdlessstdbasic_stringr,class stdallocator,class
    stdallocatoriterator stdmapstdbasic_stringr,class stdallocator,int,struct
    stdlessstdchar_traits,class stdallocator
    ,class stdallocatorinsert(class
    std_Treestdchar_traits,class stdallocator
    ,struct stdpairuct stdchar_traits,class
    stdallocator,int,struct stdmapstdbasic_stringr,class stdallocator,int,struct
    stdlessstdchar_traits,class stdallocator
    ,class stdallocator_Kfn,struct
    stdlessstdchar_traits,class stdallocator
    ,class stdallocatoriterator,const
    struct stdpairct stdchar_traits,class
    stdallocator,int )' cannot convert
    parameter 2 from 'struct stdpair'
    to 'const struct stdpairstdbasic_stringr,class stdallocator,int '

38
  • Dont be scared off too much by the problems with
    C templates. Templates are still a good idea!
    But we need to remember that templates are best
    used for small, simple container classes. Large
    and complicated templates (such as the map
    example above) often lead to code bloat and
    difficult error messages.
  • Simple classes, like list and vector, tend to
    work very well in practice. Well explore these
    standard library classes in the next lecture.
Write a Comment
User Comments (0)
About PowerShow.com