Template Functions and typedef

I was curious as to what the differences and advantages of a template function over typedef?

Maybe I don't understand but it seems like the whole purpose of a template function is so that I can write the code and then use any data type I want with it. But with typedef I can use a typedef placeholder when writing code in all the places that accept a data type and then just adjust the one line of code depending on what data type it needs.
But with typedef I can use a typedef placeholder when writing code in all the places that accept a data type and then just adjust the one line of code depending on what data type it needs.


Absolutely, as long as you only need one data type. Consider
1
2
typedef int T;
T add(T a, T b) { return a + b; }

How could you add two doubles on the next line? Note: you cannot redefine T.

Using type aliases also puts the burden on the caller to remember to define or redefine T correctly. Because types can be silently converted, such a construction would be a source of bugs.

If add was a template, each use with a different type would create ("implicitly instantiate") a new function accepting that type. In other words, the compiler would automatically create two overloaded versions of the same function -- one accepting ints, and the other accepting doubles.

Templates are more versatile than that. To a limited extent, recursive template instantiation can be used to generate C++ code at compile time. You can write programs using templates that run at compile time, and whose output is C++ code; the "if-statement" in such constructs is overload selection.

These techniques allow type-safe generic data structures and functions, as well as certain optimizations. Generic code is very important to modern C++. Quite a large portion of the standard library is generic code that makes very heavy use of templates.
Last edited on
Oh! So one of the biggest downfalls I'm seeing is that if you have some code and you want it to work on some data of some type a function or class using typedef has to be decided by the programmer prior to its implementation and if you needed to work on several different data types you would actually have to completely copy the class/function and assign that one the other data type? However, a template allows me to just kind of write it up and anytime I implement the class/function I don't have to reassign the data type because the template accepts whatever current type is being passed to it and works off of that?
typedef and the preferred type alias using alias = type; do not solve the same problems as templates.

I don't have to reassign the data type because the template accepts whatever current type is being passed to it and works off of that?

Yes, because for each template type you use, an entirely new function using that type gets created based off of that template. A similar process occurs for template classes.

For each template instantiation, you can imagine that the template parameters go in place of their corresponding placeholders. This is template substitution.

The terribly contrived "add" example above will work for any type, as long as that type can be added (and some other more subtle things). If you substitute some T for which this condition doesn't hold, you'll get a substitution failure. Only if there are no other available alternatives, the code won't compile.

Last edited on
> I don't have to reassign the data type
> because the template accepts whatever current type is being passed to it and works off of that?

Yes. But that is only part of the story; the template mechanism accepts whatever argument is passed and makes intelligent decisions (with non-template functions also thrown into the mix) based on the argument.

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

// overload one for generic simple types
template < typename T > bool less( const T& a, const T& b ) { return a < b ; }

// overload two specifically (non-template) for c-style strings
bool less( const char* a, const char* b ) { return std::strcmp(a,b) < 0 ; }

// overload three for generic arrays of the same kind
template < typename T, std::size_t N > bool less( const T(&a)[N], const T(&b)[N] )
{
    for( std::size_t i = 0 ; i < N ; ++i )
    {
        if( less( a[i], b[i] ) ) return true ;
        else if( less( b[i], a[i] ) ) return false ;
    }

    return false ;
}

int main()
{
    int i = 7, j = 8 ;
    std::cout << std::boolalpha << less(i,j) << '\n' ; // overload one

    char cstr1[10] = "abcd" ;
    char cstr2[10] = "abcde" ;
    std::cout << less( cstr1, cstr2 ) << '\n' ; // overload two

    int arr1[8] = { 0, 1, 2, 3, 4, 5 } ;
    int arr2[8] = { 0, 1, 2, 7, 4, 5 } ;
    std::cout << less( arr1, arr2 ) << '\n' ; // overload three
                                // (which calls overload one for each element)

    char arr2d_x[][5] = { "abcd", "efgh", "ijkl", "mnop" } ;
    char arr2d_y[][5] = { "abcd", "Efgh", "IJKL", "mnop" } ;
    std::cout << less( arr2d_x, arr2d_y ) << '\n' ; // overload three
                                // (which calls overload two for each element)

    int arr2d_i[7][5] = { { 0, 1, 2, 3 }, { 4, 5, 6, 1 }, { 7, 8, 9 } } ;
    int arr2d_j[7][5] = { { 0, 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 9 } } ;
    std::cout << less( arr2d_i, arr2d_j ) << '\n' ; // overload three
                                // (which calls overload three for each sub-array)
                                // (which calls overload one for each element)
}

http://coliru.stacked-crooked.com/a/97916ce2bf3d391e
http://rextester.com/HYKKS90827
Topic archived. No new replies allowed.