欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++11中的變長(zhǎng)模板的示例詳解

 更新時(shí)間:2023年02月06日 09:47:56   作者:Shawn-Summer  
C++中的變長(zhǎng)模板真的是又臭又長(zhǎng),晦澀難懂,但是確實(shí)有些STL庫(kù)就是這么寫的。本文就來(lái)和大家聊聊C++11中這些變長(zhǎng)模塊的使用,需要的可以參考一下

1.C99中的變長(zhǎng)函數(shù)

宏函數(shù)可以實(shí)現(xiàn)變長(zhǎng):就是采用C99中的變長(zhǎng)宏__VA_ARGS__,如下所示為C99代碼:

#include<stdio.h>
#define LOG(...){\
    fprintf(stderr,"%s: Line %d:\t",__FILE__,__LINE__);\
    fprintf(stderr,__VA_ARGS__);\
    fprintf(stderr,"\n");\
}
int main()
{
    int x=3;
    LOG("x=%d",x);//D:\study\c++\c++11\__VA_ARGS__.cpp: Line 10:    x=3
}

普通函數(shù)也可以實(shí)現(xiàn)變長(zhǎng)

#include<stdio.h>
#include<stdarg.h>

???????double SumOfFloat(int count,...)
{
    va_list ap;
    double sum=0;
    va_start(ap,count);//使得ap初始化為參數(shù)列表的handle
    for(int i=0;i<count;i++)
        sum+=va_arg(ap,double);//每次讀取sizeof(double)的字節(jié)
    va_end(ap);
    return sum;
}
int main()
{
    printf("%f\n",SumOfFloat(3,1.2f,3.4,5.6));//10.200000
}

注意看上面代碼中,SumOfFloat中的第一個(gè)參數(shù)count,這就意味著,你必須告訴函數(shù)它的參數(shù)的具體個(gè)數(shù),這就不太方便,例如SumOfFloat(3,1.2f,3.4,5.6)中的第一個(gè)參數(shù)3。

2.C++11中的變長(zhǎng)函數(shù)

C++11實(shí)現(xiàn)上面功能的一種方法是:使用initializer_list作為函數(shù)形參

#include<iostream>
#include<initializer_list>
using namespace std;

double SumOfFloat(const initializer_list<double>& l)
{
    double sum=0;
    for(auto &val:l)
        sum+=val;
    return sum;
}
int main()
{
    printf("%f\n",SumOfFloat({1.2f,3.4,5.6}));//10.200000
}

這種思路感覺有點(diǎn)投機(jī)取巧,不太好

另一種方法是使用變長(zhǎng)模板

#include<iostream>
template<typename... T>double SumOfFloat(T...);//模板函數(shù)聲明

template<typename ...Args>//遞歸定義
double SumOfFloat(double a,Args... args)
{
    return a+SumOfFloat(args...);
}

double SumOfFloat(double a){return a;}//邊界條件
int main()
{
    printf("%f\n",SumOfFloat(1.2f,3.4,5.6));//10.200000
}

先不對(duì)上述代碼做解釋,只要知道<typename... T>中的T叫做模板參數(shù)包,它是類型的,是一種特殊的類型,它可以被推導(dǎo)為多個(gè)類型的pack。即SumOfFloat(1.2f,3.4,5.6)會(huì)將T推導(dǎo)為double,double,double的一種pack類型。

除此之外,必須知道這種設(shè)計(jì)必須是遞歸定義的。

3.詳解變長(zhǎng)模板

首先講一個(gè)前置概念:SFINAE.

3.1 更一般的SFINAE規(guī)則

在C++98中,我們就有SFINAE法則:Substitution failure is not an error

即匹配失敗不算失敗,在C++中就是,即使模板展開失敗,也不會(huì)報(bào)錯(cuò)

struct Test
{
    typedef int FOO;
};
template<typename T>
void foo(typename T::FOO){}

template<typename T>
void foo(T){}

int main()
{
    foo<Test>(10);
    foo<int>(10);
}

上面代碼在C++98中也可以通過(guò)編譯,上面foo(typename T::FOO)中typename顯式的表示,T::FOO是一個(gè)類型,在foo<int>(10);中,編譯器會(huì)嘗試用 第一個(gè)模板函數(shù)來(lái)匹配它,但是會(huì)發(fā)現(xiàn)int::FOO錯(cuò)誤,但是編譯器不會(huì)報(bào)錯(cuò),這就是SFINAE

在C++11中對(duì)SFINAE規(guī)則做了放松,

template<int I>
struct A {};
char xxx(int);
char xxx(float);
template<typename T>A<sizeof(xxx((T)0))> f(T){}
int main()
{
    f(1);
}

有一些C++98編譯器會(huì)對(duì)上式報(bào)錯(cuò),這是因?yàn)樗鼈冋J(rèn)為模板參數(shù)過(guò)于復(fù)雜,即這里的sizeof(xxx((T)0)),不過(guò)現(xiàn)在的編譯器都狠優(yōu)秀,它們可以完成這種表達(dá)式的推導(dǎo),C++11的標(biāo)準(zhǔn)是:只要表達(dá)式中沒(méi)有出現(xiàn)外部于表達(dá)式本身的元素,編譯器都可以完成推導(dǎo)

3.2 模板參數(shù)包的概念

接下來(lái)的內(nèi)容會(huì)有一定難度。

我們知道,在C++98中模板參數(shù)有3種:類型的,非類型的,模板類型的。

template<typename T,int i,template<typename> class A>中T就是類型的,i是非類型的,A是模板類型的。

在C++11中,我們?yōu)榱酥С肿冮L(zhǎng)的模板,我們加入一種新的模板參數(shù):模板參數(shù)包。

所以在C++11中模板參數(shù)有4種:類型的,非類型的,模板類型的和模板參數(shù)包。

而模板參數(shù)包又可以細(xì)分為3種:類型的模板參數(shù)包,非類型的模板參數(shù)包,模板類型的模板參數(shù)包。

模板參數(shù)包是一種pack,下面我們從模板推導(dǎo)角度來(lái)解釋這種pack:

1.(類型的)模板參數(shù)包

template <typename T1,typename T2>class B{};
template <typename... A>class Template: private B<A...>{};
Template<X,Y> xy;

上面中,<typename...A>中A是 (類型的)模板參數(shù)包,它可以接收任意多個(gè)類型參數(shù)作為模板參數(shù),具體來(lái)說(shuō),Template<X,Y>會(huì)將A推導(dǎo)為X和Y類型的pack。

B<A...>中A...是一種包擴(kuò)展,它是模板參數(shù)包unpack的結(jié)果。由于A被推導(dǎo)為X和Y的pack,所以A...就被具體解釋為X,Y,然后具體化為B<X,Y>。

如果我們使用Template<X,Y,Z> xyz就會(huì)引發(fā)推導(dǎo)錯(cuò)誤,沒(méi)有任何一個(gè)模板適配,這是因?yàn)榇藭r(shí)A...被解釋為3個(gè)類型:X,Y,Z,它無(wú)法和B匹配。

2.(非類型的)模板參數(shù)包

template<int i,long j,unsigned int k>class B{};
template<int ...A> struct Pack: private B<A...>{};
Pack<1,0,2> data; 

<int ... A>中A是 (非類型的)模板參數(shù)包,它可以接收分離多個(gè)非類型參數(shù)作為模板參數(shù),具體來(lái)說(shuō),Pack<1,0,2>會(huì)將A推導(dǎo)為整值1,0,2的pack,而B<A...>中A...是一種包擴(kuò)展,由于A推導(dǎo)為整值1,0,2的pack,所以A...被具體解釋為1,0,2,然后具體化為B<1,0,2>

3.(模板類型的)模板參數(shù)包

template <typename T> class A;
template <typename T> class B;
template<template<typename> class T1,template<typename> class T2> class C{};
template<template<typename> class ...T> struct Pack:private C<T...>{};
Pack<A,B> data;

<template<typename> class ...T>中T是 (模板類型的)模板參數(shù)包,它可以接收多個(gè)模板作為模板參數(shù),具體來(lái)說(shuō),Pack<A,B>會(huì)將T推導(dǎo)為A和B的pack,而C<T...>中T...就是一種包擴(kuò)展,由于T推導(dǎo)為A和B的pack,所以T...就被具體解釋為A,B,然后具體化為C<A,B>

3.3 三個(gè)簡(jiǎn)單的例子

變長(zhǎng)模板必須采用遞歸設(shè)計(jì),下面是3個(gè)簡(jiǎn)單的例子,請(qǐng)仔細(xì)閱讀。

1.(類型的)模板參數(shù)包的使用

下面給出C++11中tuple的簡(jiǎn)單實(shí)現(xiàn),

template<typename... Elements> class tuple;//模板聲明

template <typename Head,typename... Tail>//遞歸定義
class tuple<Head,Tail...>:private tuple<Tail...>
{
    Head head;
};
template<> class tuple<>{};//邊界條件

同樣也是遞歸定義,這種遞歸的設(shè)計(jì)就是變長(zhǎng)模板最晦澀的地方。

當(dāng)實(shí)例化tuple<double,int,char,float>類時(shí),

第一次:Head被推導(dǎo)為double,Tail...被推導(dǎo)為int,char,float

第二次:Head被推導(dǎo)為int,Tail...被推導(dǎo)為char,float

第三次:Head被推導(dǎo)為char,Tail...被推導(dǎo)為float

第三次:Head被推導(dǎo)為float,Tail...被推導(dǎo)為空

最后由class tuple<>進(jìn)行遞歸構(gòu)造出模板

2.(非類型的)模板參數(shù)包的使用

#include<iostream>
using namespace std;

template<long... nums> struct Multiply;//模板聲明

template<long first,long... last>//遞歸定義
struct Multiply<first,last...>
{
    static const long val=first*Multiply<last...>::val;
};
template <>//邊界條件
struct Multiply<>
{
    static const long val=1;
};
int main()
{
    cout<<Multiply<2,3,4,5>::val<<endl;
    cout<<Multiply<22,44,66,88,9>::val<<endl;
}

上面這種編程方式,叫做模板元編程,他將乘法的計(jì)算放到模板推導(dǎo)過(guò)程中,就是把計(jì)算過(guò)程放在編譯階段,這樣運(yùn)行時(shí)就不需要計(jì)算了

3.(模板類型的)模板參數(shù)包的使用

template <typename T> class Module1{};
template <typename T> class Module2{};

template<typename I,template<typename>class ... B>struct Container;//模板聲明
template<typename I,template<typename> class A,template<typename> class... B>
struct Container<I,A,B...>//遞歸定義
{
    A<I> a;
    Container<I,B...> b;
};
template<typename I> struct Container<I>{};//邊界條件

int main()
{
    Container<int,Module1,Module2> a;
}

3.4 函數(shù)參數(shù)包

函數(shù)參數(shù)包是在寫變長(zhǎng)模板函數(shù)中的一個(gè)概念

函數(shù)參數(shù)包也是一種pack型變量,它也存在unpack,包擴(kuò)展的概念。

void g(int,char,double);
template<typename ... T> 
void f(T... args)
{
    g(args...);
}
f(1,'c',1.2);

在<typename ... T>中的T是 (類型的)模板參數(shù)包 。

在f(T... args)中T...叫做 包擴(kuò)展

在f(T... args)中的args是一種類型為T...的變量,它叫函數(shù)參數(shù)包

在g(args...)中的args...也是一種包擴(kuò)展,它是將argsunpack后的產(chǎn)物

例如,這里f(1,'c',1.2)就會(huì)將T推導(dǎo)為int,char,double的pack,于是T...就被具體解釋為int,char,double,然后args就是類型為T...的一種變量,args的值是1,'c',1.2的pack,則我們可以在f中調(diào)用g(args...)完成對(duì)args的unpack。

下面看一個(gè),C++11中提案的prinf()函數(shù)的實(shí)現(xiàn)

#include<iostream>
#include<stdexcept>
using namespace std;

void Printf(const char*s)//邊界條件
{
    while(*s)
    {
        if(*s=='%' && *++s!='%')//確保`%%`不出現(xiàn)
            throw runtime_error("invalid format string: missing arguments");
        cout<<*s++;
    }
}
template<typename T,typename ...Args>//遞歸定義
void Printf(const char*s,T value,Args... args)
{
    while(*s)
    {
        if(*s=='%' && *++s!='%')//確保`%%`不出現(xiàn)
        {
            cout<<value;
            return Printf(++s,args...);
        }
        cout<<*s++;
    }
    throw runtime_error("extra arguments provided to Printf");
}
int main()
{
    Printf("hello %s\n",(string)"world");
}

變長(zhǎng)模板的難點(diǎn)在于我們不知道參數(shù)的個(gè)數(shù),我們必須采用遞歸定義,就像上面的Printf就是遞歸定義的,采用的是數(shù)學(xué)歸納法,可以細(xì)細(xì)品味一下上面那段代碼。

3.5 包擴(kuò)展的進(jìn)階

...符號(hào)可以放在意想不到的地方,例如:

template<typename... A> class T:private B<A>...{};//#1
template<typename... A> class T:private B<A...>{};//#2

對(duì)于實(shí)例化T<X,Y>,#1會(huì)被解釋為

class T<X,Y> class T:private B<X>,private B<Y>{};

#2會(huì)被解釋為

class T<X,Y> class T:private B<X,Y>{};

看一下下面這些例子:

#include<iostream>
using namespace std;

template<typename... T>
void DummyWrapper(T... t){};

template<typename T>
T pr(T t){
    cout<<t;
    return t;
}
template<typename... A>
void VTPrint(A... a)
{
    DummyWrapper(pr(a)...);
}
int main()
{
    VTPrint(1,", ",1.2,", abc\n");
}

上面這段代碼,某些編譯器(例如g++)的結(jié)果是逆序的:

, abc 
1.2, 1

應(yīng)該是,不同的編譯器可能包擴(kuò)展的順序不太一樣,有些的逆序的。

下面我們看一段晦澀難懂的代碼

#include<iostream>
#include<tuple>
using namespace std;

template<typename A,typename B> 
struct S
{
    int a=1;
};

template<
    template<typename...> class T, typename... TArgs,
    template<typename...> class U, typename... UArgs
    >
    struct S< T<TArgs...> , U<UArgs...> >{int a=2;};

int main()
{
    S<int,float> p;
    S<tuple<int,char>,tuple<float>> s;
    //S<tuple,int,char,tuple,float> s;編譯出錯(cuò) 
    cout<<s.a<<endl;//2
}

注意上面這段代碼中,最終輸出是2,奇怪的地方在于,S<tuple<int,char>,tuple<float>>如何匹配第二個(gè)模板呢?這種設(shè)計(jì)是約定俗稱的,沒(méi)有任何原因,記住上面這種巧妙的設(shè)計(jì)就行了。

3.6 sizeof...()的使用

sizeof...()其實(shí)狠簡(jiǎn)單的,它就是獲得pack中的變量的個(gè)數(shù),有些用的

#include<cassert>
#include<iostream>
using namespace std;

template<typename... A>
void Print(A... arg)
{
    assert(false);
}

void Print(int a1,int a2,int a3,int a4,int a5,int a6)
{
    cout<<a1<<", "<<a2<<", "<<a3<<", "<<a4<<", "<<a5<<", "<<a6<<endl;
}

template<class... A>int Vaargs(A... args)
{
    int size=sizeof...(args);//或者sizeof...(A)

    switch (size)
    {
        case 0:Print(99,99,99,99,99,99);
                break;
        case 1:Print(99,99,args...,99,99,99);
                break;
        case 2:Print(99,99,args...,99,99);
                break;
        case 3:Print(args...,99,99,99);
                break;
        case 4:Print(99,args...,99);
            break;
        case 5:Print(99,args...);
            break;
        case 6:Print(args...);
            break;
        default:
            Print(0,0,0,0,0,0);
    }
}
int main()
{
    Vaargs();//99, 99, 99, 99, 99, 99
    Vaargs(1);//99, 99, 1, 99, 99, 99 
    Vaargs(1,2);//99, 99, 1, 2, 99, 99
    Vaargs(1,2,3);//1, 2, 3, 99, 99, 99  
    Vaargs(1,2,3,4);//99, 1, 2, 3, 4, 99   
    Vaargs(1,2,3,4,5);//99, 1, 2, 3, 4, 5 
    Vaargs(1,2,3,4,5,6);//1, 2, 3, 4, 5, 6  
    Vaargs(1,2,3,4,5,6,7);//0, 0, 0, 0, 0, 0 
}

3.7 變長(zhǎng)模板和完美轉(zhuǎn)發(fā)的配合

#include<iostream>
using namespace std;

struct A
{
    A(){};
    A(const A&a){cout<<"Copy Constructed "<<__func__<<endl;}
    A(A&& a){cout<<"Move Constructed "<<__func__<<endl;}
};

struct B
{
    B(){};
    B(const B&b){cout<<"Copy Constructed "<<__func__<<endl;}
    B(B&& b){cout<<"Move Constructed "<<__func__<<endl;}
};

template<typename... T> struct  MultiTypes;//模板聲明

template<typename T1,typename... T>//遞歸定義
struct MultiTypes<T1,T...>: public MultiTypes<T...>
{
    T1 t1;
    MultiTypes<T1,T...>(T1 a,T... b):t1(a),MultiTypes<T...>(b...)
    {
        cout<<"MultiTypes<T1,T...>(T1 a,T... b)"<<endl;
    }
};

template<> struct MultiTypes<>//邊界條件
{
    MultiTypes<>(){cout<<"MultiTypes<>()"<<endl;}
};

template<template<typename...> class VariadicType,typename... Args>
VariadicType<Args...> Build(Args&& ... args)
{
    return VariadicType<Args...>(std::forward<Args>(args)...);
}

int main()
{
    A a;
    B b;
    Build<MultiTypes>(a,b);
    //等價(jià)于Build<MultiTypes,A,B>(a,b);
}

MultiTypes<>()
MultiTypes<T1,T...>(T1 a,T... b)
MultiTypes<T1,T...>(T1 a,T... b)

沒(méi)啥好說(shuō)的,這就是完美轉(zhuǎn)發(fā),它根部就不調(diào)用移動(dòng)構(gòu)造和拷貝構(gòu)造函數(shù),完全都是靠引用傳遞的

以上就是C++11中的變長(zhǎng)模板的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于C++11變長(zhǎng)模板的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Qt實(shí)現(xiàn)SqlTableModel映射組件應(yīng)用小結(jié)

    Qt實(shí)現(xiàn)SqlTableModel映射組件應(yīng)用小結(jié)

    在Qt中提供了QSqlTableModel模型類,它為開發(fā)者提供了一種直觀的方式來(lái)與數(shù)據(jù)庫(kù)表格進(jìn)行交互,本文就來(lái)介紹一下Qt實(shí)現(xiàn)SqlTableModel映射組件應(yīng)用小結(jié),感興趣的可以了解一下
    2023-12-12
  • C++如何將二叉搜索樹轉(zhuǎn)換成雙向循環(huán)鏈表(雙指針或數(shù)組)

    C++如何將二叉搜索樹轉(zhuǎn)換成雙向循環(huán)鏈表(雙指針或數(shù)組)

    這篇文章主要介紹了C++如何將二叉搜索樹轉(zhuǎn)換成雙向循環(huán)鏈表(雙指針或數(shù)組),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • C/C++實(shí)現(xiàn)貪吃蛇逐步運(yùn)動(dòng)效果

    C/C++實(shí)現(xiàn)貪吃蛇逐步運(yùn)動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了C/C++實(shí)現(xiàn)貪吃蛇逐步運(yùn)動(dòng)效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • C語(yǔ)言的空類型指針,空指針,野指針詳解

    C語(yǔ)言的空類型指針,空指針,野指針詳解

    這篇文章主要介紹了C語(yǔ)言的空類型指針,空指針,野指針的詳解及用法示例,介紹了其相關(guān)概念,然后分享了幾種用法,具有一定參考價(jià)值。需要的朋友可以了解下。
    2021-09-09
  • OpenGL實(shí)現(xiàn)貝塞爾曲線或曲面

    OpenGL實(shí)現(xiàn)貝塞爾曲線或曲面

    這篇文章主要為大家詳細(xì)介紹了OpenGL實(shí)現(xiàn)貝塞爾曲線或曲面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)輔助器(附源碼)

    C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)輔助器(附源碼)

    數(shù)獨(dú)是源自瑞士的一種數(shù)學(xué)游戲。是一種運(yùn)用紙、筆進(jìn)行演算的邏輯游戲。本文將利用C語(yǔ)言制作一個(gè)數(shù)獨(dú)輔助器,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-01-01
  • C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目

    C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • C語(yǔ)言實(shí)現(xiàn)圖的遍歷之深度優(yōu)先搜索實(shí)例

    C語(yǔ)言實(shí)現(xiàn)圖的遍歷之深度優(yōu)先搜索實(shí)例

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)圖的遍歷之深度優(yōu)先搜索實(shí)例,采用不同的方法實(shí)現(xiàn)了深度優(yōu)先搜索算法,有不錯(cuò)的借鑒價(jià)值,需要的朋友可以參考下
    2014-09-09
  • C語(yǔ)言背包問(wèn)題求解全過(guò)程(貪心方法)

    C語(yǔ)言背包問(wèn)題求解全過(guò)程(貪心方法)

    背包問(wèn)題是一個(gè)經(jīng)典的動(dòng)態(tài)規(guī)劃問(wèn)題,而貪心算法是一種常用的解決背包問(wèn)題的方法,這篇文章主要給大家介紹了關(guān)于C語(yǔ)言背包問(wèn)題求解(貪心方法)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • C++利用遞歸實(shí)現(xiàn)走迷宮

    C++利用遞歸實(shí)現(xiàn)走迷宮

    這篇文章主要為大家詳細(xì)介紹了C++利用遞歸實(shí)現(xiàn)走迷宮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03

最新評(píng)論