C++元編程語言初步入門詳解

模板

由於模板元編程需要以面向對象為基礎,所以如有疑問之處可以先補充一點C++面向對象的知識:

C++面向對象這一篇就夠瞭

泛型初步

由於C++是靜態強類型語言,所以變量一經創建,則類型不得更改。如果我們希望創建一種應用廣泛地復數類型,那麼相應地需要基於intfloatdouble這些基礎類型逐一創建,十分麻煩。泛型編程便是為瞭簡化這一過程而生。

能夠容納不同數據類型作為成員的類被成為模板類,其基本方法為在類聲明的上面加上一行模板聲明代碼

template<typename T>,下一行為class myClass,其調用過程為myClass<T> m。

列舉案例如下

#include<iostream>
using namespace std;
template<typename C>
struct Abstract{
    C real;         //real為C類型
    C im;
    Abstract(C inReal, C inIm){
        real = inReal;
        im = inIm;
    }
    void printVal(){
        cout<<"Abstract:"<<real<<"+"<<im<<"i"<<endl;
    };
    Abstract& multi(Abstract val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    };
};
int main(){
    Abstract<float> fTemp{1,2};//C類型為float
    fTemp.multi(fTemp);
    fTemp.printVal();
    system("pause");
    return 0;
}

函數模板

當然,上述multi並不能實現兩個不同類型的Abstract之間的相乘,所以可以將multi函數改為

    template<typename T>
    Abstract<C>& multi(Abstract<T> val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    }

這樣就能夠實現如下功能。

int main(){
    Abstract<float> fTemp{1,2};
    Abstract<int> iTemp{1,2};
    fTemp.multi(iTemp);
    fTemp.printVal();
    getReal(fTemp);
    system("pause");
    return 0;
}

友元

模板類具備一部分普通類的性質,比如struct和class的區別,public、protected、private的性質,以及友元等。模板的聲明特性也可以應用在函數中,例如

#include<iostream>
using namespace std;
template<typename C>
class Abstract{
    C real;
    C im;
public:
    Abstract(C inReal, C inIm){
        real = inReal;
        im = inIm;
    }
    void printVal(){
        cout<<"Abstract:"<<real<<"+"<<im<<"i"<<endl;
    };
    Abstract& multi(Abstract val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    }
    template<typename T> friend void getReal(Abstract<T> num);  //聲明友元
};
template<typename C>
void getReal(Abstract<C> num){
    cout<<num.real<<endl;
}
int main(){
    Abstract<float> fTemp{1,2};
    fTemp.multi(fTemp);
    fTemp.printVal();
    getReal(fTemp);
    system("pause");
    return 0;
}

需要註意的一點是,在模板類中聲明友元,其前綴<typename T>中的類型標識不得與已有的類型標識重復,否則編譯無法通過。

由於函數模板可以針對不同的數據類型進行求解操作,是對函數或者方法實例的抽象,所以又被稱為算法。

模板參數

如果將模板理解為一種類型聲明的函數,那麼模板也應該具備一些函數具備的功能。首先其模板參數中可以包含實際類型參數,例如

template<typename T, int max>
class Test{}

其調用時可以寫為

Test<int,256> pixel;

模板同樣支持默認參數,即可以實現如下形式

template<typename T=int, int max=256>
class Test{}
Test pixle;

除瞭數據類型、值之外,模板本身也可以作為模板參數,例如下面的形式是合法的。

template<typename T, template<typename> class C>
struct Test{
    C<T>* val;
    Test(C<T>* inVal){
        val = inVal;
    }
};
int main(){
    Abstract<int> fTemp{1,2};
    Test<int,Abstract> test(&fTemp);
    test.val->printVal();
    system("pause");
    return 0;
}

其結果為

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
Abstract:1+2i
請按任意鍵繼續. . . 

需要註意的一點是,在模板類中定義的模板類,需要進行實例化,否則會出現錯誤,所以在Test中,以指針形式創建瞭模板類。

類型函數

以數據類型為輸入或輸出的函數即為類型函數,在C語言中,sizeof便是一種類型函數,其輸入為數據類型,輸出為數據類型所需要的內存空間。

在C++11中,using可以實現數據類型賦予的功能,其使用方法與typedef相似

template<typename T>
struct Test{
    using type = T;
}

元編程的基本概念

元編程是泛型編程的一個超集,兩者的本質均是針對不同數據類型的算法,後者則更關註傳入參數的廣泛性。如果將元編程分為四個層次

  • 無計算
  • 運算符連接的運算
  • 編譯時具備選擇等非遞歸計算
  • 編譯時具備遞歸運算

那麼泛型編程可以作為第一類元編程,或者說更加關註的是參數的傳入傳出過程,而元編程則更關註不同數據類型的選擇過程。

例如,我們可以實現一個最多包含三個元素的元組Tuple,其思路為,三元元素可以看成是一個二元元組與一個參數的組合;二元元組可以看成是一元元組與參數的組合;一元元組則是一個基本數據類型的變量。在這個元組的實現過程中,除瞭賦值過程實現泛型之外,也需要判斷當前所實現的元組元素個數,如果其初始化參量為3個時,需要遞歸式地創建變量,直到賦值參數為1個。則其實現如下

class Nil{};
//主模板
template<typename T1=Nil, typename T2=Nil, typename T3=Nil>
struct Tuple : Tuple<T2,T3>{
    T1 x;
    using Base = Tuple<T2,T3>;      //三元元組以二元元組為基礎
    //返回值為Tuple<T2,T3>指針類型的base()函數
    //static_cast將this轉化為Base*類型
    Base* base(){return static_cast<Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    //構造函數繼承二元元組,在構造本類中x的同時,構造基類Tuple<T2,T3>
    Tuple(const T1& t1, const T2& t2, const T3& t3)
        :Base{t2,t3},x{t1}{}
};
template<typename T1>
struct Tuple<T1>{
    T1 x;
};
template<typename T1, typename T2>
struct Tuple<T1,T2> : Tuple<T2>{
    T1 x;
    using Base = Tuple<T2>;
    Base* base(){return static_cast<const Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    Tuple(const T1& t1,const T2& t2):Base{t2}, x{t1}{}
};
template<typename T1, typename T2, typename T3>
void print_elements(ostream& os, const Tuple<T1,T2,T3>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1, typename T2>
void print_elements(ostream& os, const Tuple<T1,T2>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1>
void print_elements(ostream& os, const Tuple<T1>& t){
    os<<t.x;
}
//運算符重載
template<typename T1, typename T2, typename T3>
ostream& operator<<(ostream& os, const Tuple<T1,T2,T3>& t){
    os<<"{";
    print_elements(os,t);
    os<<"}";
    return os;
}
int main(){
    Tuple<int,double,char> x{1,2.5,'a'};
    cout<<x<<endl;
    system("pause");
    return 0;
}

其輸出結果為

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{1,2.5,a}

可變參數模板

上述實現過程非常繁瑣,而且限制瞭元組中的元素個數,如果標準庫中用上述的書寫風格,那麼標準庫除瞭這個元組之外也寫不瞭其他的東西瞭。好在C++模板提供瞭可變參數的功能,例如,我們可以先將打印模板函數寫為

//typename... T 代表可變參數
template<typename T1, typename... T>
void print_elements(ostream& os, const Tuple<T1,T...>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1>
void print_elements(ostream& os, const Tuple<T1>& t){
    os<<t.x;
}
template<typename... T>
ostream& operator<<(ostream& os, const Tuple<T...>& t){
    os<<"{";
    print_elements(os,t);
    os<<"}";
    return os;
}

其輸出結果為

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{1,2.5,a}
請按任意鍵繼續. . . 

然後將Tuple也做相同的更改

template<typename T1, typename... T>
struct Tuple : Tuple<T...>{
    T1 x;
    using Base = Tuple<T...>;      //N+1元元組以N元元組為基
    Base* base(){return static_cast<Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    //註意T&...的書寫格式
    Tuple(const T1& t1, const T&... t):Base{t...},x{t1}{}
};
template<typename T>
struct Tuple<T>{
    T x;
};
/*
    print模板
*/
int main(){
    Tuple<string, double,int,char> tt("hello",1.5,1,'a');
    cout<<tt<<endl;
    system("pause");
    return 0;
}

其輸出結果為

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{hello,1.5,1,a}

以上就是C++元編程語言初步入門詳解的詳細內容,更多關於C++元編程語言初步的資料請關註WalkonNet其它相關文章!

推薦閱讀: