第20課 - 函數模板

第20課 - 函數模板

    一.C++中如何交換兩個變量的值

      
  void swap(int& a, int& b)
        {
            int t = a;
            a = b;
            b = t;
        }
        
        void swap(float& a, float& b)
        {
            float t = a;
            a = b;
            b = t;
        }
        
        void swap(char*& a, char*& b)
        {
            char* t = a;
            a = b;
            b = t;
        }

        這三個函數,除了類型不同,函數體代碼完全相同。

      C++強調代碼複用,如何解決這個代碼冗餘的問題?

    

    二.泛型編程

        2.1 不考慮具體數據類型的編程模式

            對於Swap函數可以考慮下面泛型的寫法
                
                void Swap(T&a, T&b)
                {
                    T t = a;
                    a = b;
                    b = t;
                }
            Swap泛型寫法中T不是一個具體的數據類型,而是泛指的任意數據類型。
        

        2.2 C++中的泛型編程->函數模板

            2.2.1 提供一種特殊的函數可用不同類型進行調用

            2.2.2 看起來和普通函數很相似,區別是類型可被參數化

             
               template <typename T>
                void Swap(T&a, T&b)
                {
                    T t = a;
                    a = b;
                     b = t;
                }
               

            2.2.3 函數模板的語法規則

                a.template關鍵字用於聲明開始進行泛型編程

                b.typename關鍵字用於聲明泛指類型

                

            2.2.4 函數模板的應用

                a.自動類型推導調用

                b.具體類型顯示調用


                int a = 1;
                int b = 1;
                
                Swap(a,b);    -->自動類型推導:a和b均是int,因此參數T爲int
                
                flaot fa = 3;
                flaot fb = 4;
            
                Swap<float>(fa, fb);-->顯示類型調用:用float替換參數類型T
           
Source Example 2.2.4:
                #include <iostream>

                /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                template <typename T>

                void Swap(T& a, T& b)
                {
                    T t = a;
                    a = b;
                    b = t;
                }

                int main(int argc, char** argv) {
                    
                    int a = 1;
                    int b = 0;
                    
                    Swap (a, b);
                    printf("a = %d, b = %d\n", a , b );
                    
                    float c = 1.2;
                    float d = 1.3;
                    
                    Swap<float>(c, d);
                    printf("c = %f, d = %f\n", c , d );
                    
                    char ca = 'a';
                    char cb = 'b';
                    
                    Swap (ca, cb);
                    printf("ca = %c, cb = %c\n", ca , cb );
                    
                    return 0;
                } 

            2.2.5 泛型算法:

              
 Source Example 2.2.5(選擇排序):
                    #include <iostream>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                    template <typename T>
                    void Swap(T& a, T& b)
                    {
                        T& t = a;
                        a = b;
                        b = t;
                    }

                    template <typename T>
                    void SelectSort(T array[], int length)
                    {
                        for (int i = 0; i < length; i++)
                        {
                            T min = array[i];
                            int index = i;
                            
                            for (int j = i + 1; j < length; j++)
                            {
                                if (array[j] < min)
                                {
                                    min = array[j];
                                    index = j;
                                }
                            }
                            
                            Swap(array[i], array[index]);
                        }
                    }

                    int main(int argc, char** argv) {
                        int array[] = {3,2,5,4,5};

                        SelectSort<int>(array, 5);
                        
                        for (int i = 0; i < 5; i++)
                        {
                            printf("array[%d] = %d\n", i ,array[i]);    
                        }
                            
                        return 0;
                    }

                    輸出結果如下:


                    

            2.2.6 函數模板的深入理解

                a.編譯器並不是把函數模板處理成能夠處理任意類型的函數

                b.編譯器從函數模板通過具體類型產生不同的函數

                c.編譯器會對函數模板進行兩次編譯

                    在聲明的地方對模板代碼本省進行編譯

                    在調用的地方對參數替換後的代碼進行編譯

                    

    

    三.函數重載遇上函數模板

        3.1 函數模板可以像普通函數一樣被重載

            3.1.1 C++編譯器優先考慮普通函數

            3.1.2 如果函數模板可以產生一個更好的匹配,那麼選擇模板

            3.1.3 可以通過空模板實參列表的語法限定編譯器只通過模板匹配

     
   Source Example 3.1:
            #include <iostream>

            /* run this program using the console pauser or add your own getch, system("pause") or input loop */

            int Max(int a, int b)
            {
                printf("nomanl!\n");
                return a > b ? a : b;
            }

            template<typename T>
            T Max(T a, T b)
            {
                printf("template!\n");
                return a > b ? a : b;
            }

            template<typename T>
            T Max(T a, T b,T c)
            {
                        /* A,B是未知的泛型,會調用函數模板 */
                return Max(Max(a,b), c);
            }


            int main(int argc, char** argv) {
                int a = 1;
                int b = 2;
                
                /* 優先調用普通的Max函數 */
                printf("%d\n", Max(a,b));
                /* 通過空模板實參列表限定編譯器調用模板函數 */
                printf("%d\n", Max<>(a,b));

                float fa = 3;
                float fb = 4;

                /* 由於是浮點型,會優先匹配函數模板 */
                printf("%f\n", Max(fa,fb));

                char ca = 5;
                char cb = 6;
                char cc = 7;
                
                printf("%d\n", (int)Max(ca, cb, cc));            
                
                /* 調用普通函數,因爲模板不允許進行類型轉換,普通函數可以 */
                Max('a', 100);
                return 0;
            }    
            輸出結果如下:
                
            

        3.2 注意事項

            3.2.1 函數模板不允許自動類型轉化

            3.2.2 普通函數能夠進行自動類型轉換

            

        3.3 函數模板可以定義任意多個不同的類型參數

            
            

            但是問題多個類型參數的模板可以支持自動類型推導嗎?

                當聲明的類型參數爲返回值類型時,無法進行自動類型推導。

      
     Source Example 3.3
            #include <iostream>

            /* run this program using the console pauser or add your own getch, system("pause") or input loop */

            template<typename T1,typename T2,typename RT>
            RT Add(T1 a,T2 b)
            {
                return static_cast<RT>(a + b);
            }

            int main(int argc, char** argv) {
                
                /* 顯示調用,沒有問題 */
                printf("%lf", Add<char, float, double>('a', 100));
                
                /* 推導不出來返回值類型,會報錯 */
                printf("%lf", Add('a', 100));
                
                return 0;
            }                      

            3.3.1 不完美的解決方案

                將返回類型參數聲明放到第一個參數位置,調用時只需要顯示聲明返回類型參數即可

         
   Source Example 3.3.1
                #include <iostream>

                /* run this program using the console pauser or add your own getch, system("pause") or input loop */
                using namespace std;
                
                /* 返回類型要放在第一個 */
                template<typename RT,typename T1,typename T2>
                RT Add(T1 a,T2 b)
                {
                    return static_cast<RT>(a + b);
                }

                int main(int argc, char** argv) {
                    
                    /* 顯示調用,沒有問題 */
                    printf("%lf\n", Add<double, char, float>('a', 100));
                    
                                /* 提供返回類型 */
                    printf("%lf", Add<double>('a', 100.0f));
                    
                    return 0;
                }  


                

    四.小結

        4.1 函數模板其實是一個具有相同行爲的函數家族

        4.2 函數模板可以根據類型實參對函數進行推導調用

        4.3 函數模板可以顯示的指定類型參數

        4.4 函數模板可以被重載


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章