Thread: Explicit specialization for the win

  1. #1
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193

    Post Explicit specialization for the win

    Good evening. I'm trying to do the following:

    Write a template function maxn() that takes as its arguments an array of items of
    type T and an integer representing the number of elements in the array and that
    returns the largest item in the array.Test it in a program that uses the function tem-
    plate with an array of six int value and an array of four double values.The pro-
    gram should also include a specialization that takes an array of pointers-to-char as
    an argument and the number of pointers as a second argument and that returns the
    address of the longest string. If multiple strings are tied for having the longest
    length, the function should return the address of the first one tied for longest.Test
    the specialization with an array of five string pointers.
    Code:
     
    #include <iostream> 
    #include <cstring>
    
    using namespace std; 
    
    template <typename T> 
    T maxn(const T *a, const int n);
    
    // explicit specialization
    template <> char* maxn(const char *words[], const int n);
    
    template <typename T> 
    T maxn(const T *a, const int n) 
    { 
      T max = a[0]; 
      size_t i;
      
      for(i = 1; i < n; i++) 
      { 
        if(a[i] > max) 
         max = a[i];      
      } 
      return max;      
    }     
    
    // template function should return the address of the longest string
    template <> char* maxn(const char *words[], const int n) 
    { 
       size_t tam = strlen(words[0]);
       char *longest; 
       char *str;
       for(str = words[1]; str != words[n]; str++)
       { 
         if(tam < strlen(str) )
         { 
            tam = strlen(str);
            longest = str;     
         }           
       }
       return longest;        
    }     
    
    int main()
    { 
       int a1[6] = {12, -56, 11, -98, 32, 21};
       double a2[4] = {56.12, 78.31, -99.99, 12.11};        
       
       double dmax; 
       int imax; 
       
       imax = maxn(a1, 6); 
       dmax = maxn(a2, 4);
       
       cout << "Largest integer value: " << imax << endl; 
       cout << "Largest double value: " << dmax << endl; 
       
       return 0;     
    }
    There are lot of things going wrong there:

    Code:
     
    thames@semaht ~/C++/Templates $ g++ -g -Wall maxn.cpp -o maxn -std=c++11
    maxn.cpp:10:19: error: template-id ‘maxn<>’ for ‘char* maxn(const char**, int)’ does not match any template declaration
    maxn.cpp:26:19: error: template-id ‘maxn<>’ for ‘char* maxn(const char**, int)’ does not match any template declaration
    maxn.cpp: In instantiation of ‘T maxn(const T*, int) [with T = int]’:
    maxn.cpp:50:21:   required from here
    maxn.cpp:18:12: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    maxn.cpp: In instantiation of ‘T maxn(const T*, int) [with T = double]’:
    maxn.cpp:51:21:   required from here
    maxn.cpp:18:12: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    thames@semaht ~/C++/Templates $
    Please help me fix those.

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Ah, yep. If you want your template to work for C-strings, T will have to be char. Because T** is different from T*. I do not think it is possible to actually include pointer types in the deduced type. Naturally this might mean that the C-string version should be different anyway, because it doesn't really make sense for the maxn function to return char. g++ is smart enough to not let this fly, even though you aren't using this specialization in the program.

    Code:
    maxn.cpp: In instantiation of ‘T maxn(const T*, int) [with T = int]’:
    maxn.cpp:50:21:   required from here
    maxn.cpp:18:12: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    maxn.cpp: In instantiation of ‘T maxn(const T*, int) [with T = double]’:
    maxn.cpp:51:21:   required from here
    maxn.cpp:18:12: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    Make your comparisons have uniform signedness to get rid of this.

  3. #3
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Ah, yep. If you want your template to work for C-strings, T will have to be char. Because T** is different from T*. I do not think it is possible to actually include pointer types in the deduced type. Naturally this might mean that the C-string version should be different anyway, because it doesn't really make sense for the maxn function to return char. g++ is smart enough to not let this fly, even though you aren't using this specialization in the program.
    Sorry, I'm a bit lost. That is the second code I'm writing based on explicit specialization. I'm reading Stephen Prata's book. Can you code the right template so I can picture what's going on?

    Code:
     
    #include <iostream> 
    #include <cstring>
    
    using namespace std; 
    
    template <typename T> 
    T maxn(T *a, int n);
    
    // explicit specialization
    template <> string maxn(const char *words[], int n);
    
    template <typename T> 
    T maxn(T *a, int n) 
    { 
      T max = a[0]; 
      int i;
      
      for(i = 1; i < n; i++) 
      { 
        if(a[i] > max) 
         max = a[i];      
      } 
      return max;      
    }     
    
    // template function should return the address of the longest string
    template <> string maxn(const char *words[], int n) 
    { 
       size_t tam = strlen(words[0]);
       char *longest; 
       char *str;
       for(str = words[1]; str != words[n]; str++)
       { 
         if(tam < strlen(str) )
         { 
            tam = strlen(str);
            longest = str;     
         }           
       }
       return longest;        
    }     
    
    int main()
    { 
       int a1[6] = {12, -56, 11, -98, 32, 21};
       double a2[4] = {56.12, 78.31, -99.99, 12.11};        
       // char *word;
       // const char *words[20] = {"thames", "barao", "paralelepipedo", "tetrikolas", "oi"};
       
       double dmax; 
       int imax; 
       
       imax = maxn(a1, 6); 
       dmax = maxn(a2, 4);
       
       cout << "Largest integer value: " << imax << endl; 
       cout << "Largest double value: " << dmax << endl; 
       
       // word = maxn(words, 5);
       // cout << "The longest word is " << word << endl; 
       return 0;     
    }

  4. #4
    Registered User
    Join Date
    Apr 2012
    Posts
    8
    First off, you need to make your specialisation fit the form of the template.
    Since your template is:
    Code:
    template<typename T>
    T maxn( T* a, int n );
    Your specialisation needs to simply replace T with a type name. You want to specialise on char* so the first guess is along the lines:
    Code:
    template<>
    char* maxn( char** a, int n );
    Unfortunately the compiler isn't clever enough to link that declaration with the template. However, if you define a type something like this:
    Code:
    typedef const char* const_char_p;
    template<>
    const_char_p maxn( const_char_p* a, int n );
    then the compiler will understand what you mean.

    Of course, the proper C++ way would be to use std::string rather than char*...

  5. #5
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Of course, the proper C++ way would be to use std::string rather than char*...
    I'm using a char* because the author told so.
    Now I can't pass a const char* to a char*

    Code:
    #include <iostream> 
    #include <cstring>
    
    using namespace std; 
    
    template <typename T> 
    T maxn(T *a, int n);
    
    // explicit specialization
    typedef const char* const_char_p;
    template <> 
    const_char_p maxn(const_char_p *words, int n);
    
    template <typename T> 
    T maxn(T *a, int n) 
    { 
      T max = a[0]; 
      int i;
      
      for(i = 1; i < n; i++) 
      { 
        if(a[i] > max) 
         max = a[i];      
      } 
      return max;      
    }     
    
    // template function should return the address of the longest string
    template <> 
    const_char_p maxn(const_char_p *words, int n) 
    { 
       size_t tam = strlen(words[0]);
       char *longest; 
       char *str;
       for(str = words[1]; str != words[n]; str++)
       { 
         if(tam < strlen(str) )
         { 
            tam = strlen(str);
            longest = str;     
         }           
       }
       return longest;        
    }     
    
    int main()
    { 
       int a1[6] = {12, -56, 11, -98, 32, 21};
       double a2[4] = {56.12, 78.31, -99.99, 12.11};        
       
       double dmax; 
       int imax; 
       
       imax = maxn(a1, 6); 
       dmax = maxn(a2, 4);
       
       cout << "Largest integer value: " << imax << endl; 
       cout << "Largest double value: " << dmax << endl; 
       
       return 0;     
    }
    Code:
     
      maxn.cpp: Na função ‘T maxn(T*, int) [with T = const char*]’:
    maxn.cpp:35:21: error: conversão de ‘const_char_p {aka const char*}’ para ‘char*’ inválida [-fpermissive]

  6. #6
    Registered User
    Join Date
    Apr 2012
    Posts
    8
    You should change the types that were char* to const_char_p (especially longest which is going to be returned as the latter).
    The way you are iterating through the array is a bit odd. Why not do the iteration in the same way as in the template?

    There are a couple of problems with your function as it stands:
    - words[n] is outside the array you defined.
    - when you have fixed the above problem, think about / test what happens if the last string in the array is the longest
    - think about / test what happens if the first string in the array is the longest
    Last edited by DavX; 12-19-2012 at 11:55 AM.

  7. #7
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    many thanks for the advices. Due to the fact this code is wrong with char *, I decided to skip this exercise and keep studying the book. The template explanation which concerns the compiler interpretation was invaluable.

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I've been tinkering with this. It turns out I got this wrong the first time, so I need to correct myself, anyway. You can do this:

    Code:
    #include <iostream>
    #include <cstring>
    
    template<typename type>
    type maxn(type theArray[], size_t size)
    {
        type currMax = theArray[0];
        for (size_t i = 1; i < size; i++) {
            if (currMax < theArray[i]) {
                currMax = theArray[i];
            }
        }
        return currMax;
    }
    
    template<>
    char *maxn(char *theArray[], size_t size)
    {
        char *currMax = theArray[0];
        for (size_t i = 1; i < size; i++) {
            if (std::strlen(currMax) < std::strlen(theArray[i])) {
                currMax = theArray[i];
            }
        }
        return currMax;
    }
    
    int main()
    {
        char *p;
        char *theArray[5] = {"a", "aaa", "bb", "c", "aaaa"};
        p = maxn<char *>(theArray, 5);
        std::cout << p << std::endl;
    }
    This will compile and link.

    The third line of main(), notice that I explicitly supplied the template argument, the type name. This is called "explicit template instantiation" and it might've been what the book tried to teach you. At least, it proves the exercise should work as written. You do end up passing in template arguments quite a bit in regular code. The STL container vector for instance needs to be instantiated, or the template code is useless.
    Last edited by whiteflags; 12-19-2012 at 03:14 PM.

  9. #9
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    So, in fact, the main reason my first code wasn't working, was due to the const keyword? I thought I could put const even using explicit instantiations or specializations.

  10. #10
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    So, in fact, the main reason my first code wasn't working, was due to the const keyword?
    Basically yes.

    Your original template was:
    Code:
    template <typename T>
    T maxn(const T *a, const int n)
    To match the declaration, the compiler will want to instantiate this:
    Code:
    template <> char* maxn( char *const words[], const int n )
    When I posted earlier, I fixed the code by discarding const altogether.

    Being completely honest with you, I'm not sure why the const T* in the declaration binds to the pointer and not the char part of the type. But that's how it is. It is a mystery.

    [edit]It could have something to do with "T const" and "const T" meaning the same thing, usually, but with T = char * you get a completely different type. C++ is not always so intuitive. [/edit]
    Last edited by whiteflags; 12-20-2012 at 04:12 PM.

  11. #11
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    C++ is not always so intuitive.
    indeed.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 06-22-2011, 09:17 AM
  2. Replies: 1
    Last Post: 06-21-2011, 08:39 AM
  3. template specialization
    By CodeMonkey in forum C++ Programming
    Replies: 3
    Last Post: 12-29-2008, 02:02 AM
  4. Replies: 6
    Last Post: 08-12-2007, 01:02 PM
  5. Template specialization
    By CornedBee in forum C++ Programming
    Replies: 2
    Last Post: 11-25-2003, 02:02 AM

Tags for this Thread