最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解C++11 變參模板

 更新時(shí)間:2020年08月27日 10:09:33   作者:Dabelv  
這篇文章主要介紹了C++11 變參模板的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++11,感興趣的朋友可以了解下

1.概述

變參模板(variadic template)是C++11新增的最強(qiáng)大的特性之一,它對(duì)參數(shù)進(jìn)行了高度泛化,它能表示0到任意個(gè)數(shù)、任意類型的參數(shù)。相比C++98/03,類模版和函數(shù)模版中只能含固定數(shù)量的模版參數(shù),可變模版參數(shù)無疑是一個(gè)巨大的改進(jìn)。然而由于可變模版參數(shù)比較抽象,使用起來需要一定的技巧,掌握也存在一定的難度。

2.可變模版參數(shù)的展開

可變模板參數(shù)和普通模板參數(shù)的語義是一樣的,只是寫法上稍有區(qū)別,聲明可變參數(shù)模板時(shí)需要在typename或class后面帶上省略號(hào)“…”??勺儏?shù)模版的定義形式如下:

//可變參數(shù)函數(shù)模板
template<typename... T> void f(T... args);
//可變參數(shù)類模板
template<typename... T> class ClassFoo;

上面的參數(shù)中,T為模板參數(shù)包(template parameter pack),args為函數(shù)參數(shù)包(function parameter pack),參數(shù)包里面包含了0到N(N>=0)個(gè)參數(shù)。我們無法直接獲取參數(shù)包中的每個(gè)參數(shù)的,只能通過展開參數(shù)包的方式,這是使用可變參數(shù)模版的一個(gè)主要特點(diǎn),也是最大的難點(diǎn)。

可變模版參數(shù)和普通的模版參數(shù)語義是一致的,可以應(yīng)用于函數(shù)和類,然而,函數(shù)模版不支持偏特化,所以可變參數(shù)函數(shù)模版和可變參數(shù)類模版展開參數(shù)包的方法有所不同,下面我們來分別看看他們參數(shù)包展開的方法。

2.1變參函數(shù)模版

一個(gè)簡(jiǎn)單的變參函數(shù)模板。

template <class... T> void f(T... args)
{
  cout << sizeof...(T) <<" "<< sizeof...(args) << endl; //打印函數(shù)參數(shù)包中參數(shù)個(gè)數(shù)
}

f();        //0 0
f(1, 1.2);   //2 2
f(1, 2.3, "");  //3 3

sizeof…運(yùn)算符的作用是計(jì)算參數(shù)包中的參數(shù)個(gè)數(shù),既可以作用于模板參數(shù)包T,也可以作用于函數(shù)參數(shù)包args。這個(gè)例子只是簡(jiǎn)單的將可變模版參數(shù)的個(gè)數(shù)打印出來,如果需要將參數(shù)包中的每個(gè)參數(shù)打印出來的話就需要通過其它方法了。展開函數(shù)參數(shù)包的方法一般有兩種:一種是通過遞歸函數(shù)來展開參數(shù)包,另外一種是通過逗號(hào)表達(dá)式來展開參數(shù)包。

2.1.1遞歸函數(shù)方式展開參數(shù)包

通過遞歸函數(shù)展開參數(shù)包,需要提供一個(gè)參數(shù)包展開的函數(shù)和一個(gè)遞歸終止函數(shù),遞歸終止函數(shù)正是用來終止遞歸的,來看看下面的例子。

#include <iostream>
using namespace std;

//遞歸終止函數(shù)
void print()
{
  cout << "empty" << endl;
}

//展開函數(shù)
template <class T, class ...Args> void print(T head, Args... rest)
{
  cout << "parameter " << head << endl;
  print(rest...);
}

int main(void)
{
 print(1,2,3,4);
 return 0;
}

上例會(huì)輸出每一個(gè)參數(shù),直到為空時(shí)輸出empty。展開參數(shù)包的函數(shù)有兩個(gè),一個(gè)是遞歸函數(shù),另外一個(gè)是遞歸終止函數(shù),參數(shù)包Args…在展開的過程中遞歸調(diào)用自己,每調(diào)用一次參數(shù)包中的參數(shù)就會(huì)少一個(gè),直到所有的參數(shù)都展開為止,當(dāng)沒有參數(shù)時(shí),則調(diào)用非模板函數(shù)print終止遞歸過程。遞歸調(diào)用的過程是這樣的:

print(1,2,3,4);
print(2,3,4);
print(3,4);
print(4);
print();

2.1.2逗號(hào)表達(dá)式展開參數(shù)包

遞歸函數(shù)展開參數(shù)包是一種標(biāo)準(zhǔn)做法,也比較好理解,但也有一個(gè)缺點(diǎn),就是必須要一個(gè)重載的遞歸終止函數(shù),即必須要有一個(gè)同名的終止函數(shù)來終止遞歸,這樣可能會(huì)感覺稍有不便。有沒有一種更簡(jiǎn)單的方式呢?其實(shí)還有一種方法可以不通過遞歸方式來展開參數(shù)包,這種方式需要借助逗號(hào)表達(dá)式和初始化列表。比如前面print的例子可以改成這樣:

template <class T> void printarg(T t)
{
  cout << t << endl;
}

template <class... Args> void expand(Args... args)
{
  int arr[] = {(printarg(args),0)...};
}

expand(1,2,3,4);

上面程序?qū)⒋蛴〕?,2,3,4。這種展開參數(shù)包的方式,不需要通過遞歸終止函數(shù),是直接在expand函數(shù)體中展開的, printarg不是一個(gè)遞歸終止函數(shù),只是一個(gè)處理參數(shù)包中每一個(gè)參數(shù)的函數(shù)。這種就地展開參數(shù)包的方式實(shí)現(xiàn)的關(guān)鍵是逗號(hào)表達(dá)式。我們知道逗號(hào)表達(dá)式會(huì)按順序執(zhí)行逗號(hào)前面的表達(dá)式,返回最后一個(gè)表達(dá)式結(jié)果,比如:

d = (a = b,c);

這個(gè)表達(dá)式會(huì)按順序執(zhí)行:b會(huì)先賦值給a,接著括號(hào)中的逗號(hào)表達(dá)式返回c的值,因此d將等于c。

expand函數(shù)中的逗號(hào)表達(dá)式:(printarg(args), 0),也是按照這個(gè)執(zhí)行順序,先執(zhí)行printarg(args),再得到逗號(hào)表達(dá)式的結(jié)果0。同時(shí)還用到了C++11的另外一個(gè)特性——列表初始化,通過列表初始化來初始化一個(gè)變長數(shù)組, {(printarg(args), 0)…}將會(huì)展開成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc… ),最終會(huì)創(chuàng)建一個(gè)元素值都為0的數(shù)組int arr[sizeof…(Args)]。由于是逗號(hào)表達(dá)式,在創(chuàng)建數(shù)組的過程中會(huì)先執(zhí)行逗號(hào)表達(dá)式前面的部分printarg(args)打印出參數(shù),也就是說在構(gòu)造int數(shù)組的過程中就將參數(shù)包展開了,這個(gè)數(shù)組的目的純粹是為了在數(shù)組構(gòu)造的過程展開參數(shù)包。我們可以把上面的例子再進(jìn)一步改進(jìn)一下,將函數(shù)作為參數(shù),就可以支持lambda表達(dá)式了,從而可以少寫一個(gè)遞歸終止函數(shù)了,具體代碼如下:

template<class F, class... Args> void expand(const F& f, Args&&...args) 
{
 initializer_list<int>{(f(std::forward<Args>(args)),0)...};
}
int main()
{
  expand([](int i){cout<<i<<endl;}, 1,2,3);
}

上面的例子將打印出每個(gè)參數(shù),這里如果再使用C++14的新特性泛型lambda表達(dá)式的話,可以寫更泛化的lambda表達(dá)式了:

expand([](auto i){cout<<i<<endl;}, 1,2.0,”test”);

2.2變參類模版

變參類模版是一個(gè)帶可變模板參數(shù)的模板類,比如C++11中的元祖std::tuple就是一個(gè)可變模板類,它的定義如下:

template< class... Types> class tuple;

這個(gè)可變參數(shù)模板類可以攜帶任意類型任意個(gè)數(shù)的模板參數(shù):

std::tuple<> tp;
std::tuple<int> tp1 = std::make_tuple(1);
std::tuple<int, double> tp2 = std::make_tuple(1, 2.5);
std::tuple<int, double, string> tp3 = std::make_tuple(1, 2.5,"");

變參類模板的參數(shù)包展開方式和變參函數(shù)模板的展開方式不同,變參類模板的參數(shù)包展開需要通過模板特化和繼承方式去展開,展開方式比變參函數(shù)模板要復(fù)雜。下面看一下展開變參類模板中的參數(shù)包的方法。

2.2.1偏特化與遞歸方式展開

變參類模板的展開一般需要定義兩到三個(gè)類,包括類聲明和偏特化的類模板。如下方式定義了一個(gè)基本的可變參數(shù)類模板:

//前向聲明
template<typename... Args>
struct Sum;

//基本定義
template<typename First, typename... Rest>
struct Sum<First, Rest...>
{
  enum { value = Sum<First>::value + Sum<Rest...>::value };
};

//遞歸終止
template<typename Last>
struct Sum<Last>
{
  enum { value = sizeof (Last) };
};

int main()
{
  Sum<int, char> s;

  cout<<s.value<<endl;
}

程序輸出5,即sizeof(int)+sizeof(char)??梢钥吹揭粋€(gè)基本的可變參數(shù)模板應(yīng)用類由三部分組成,前向聲明、基本定義和遞歸終止類。實(shí)際上三段式的定義也可以改為兩段式,可以將前向聲明去掉,這樣定義:

template<typename First, typename... Rest>
struct Sum
{
  enum { value = Sum<First>::value + Sum<Rest...>::value };
};

template<typename Last>
struct Sum<Last>
{
  enum{ value = sizeof(Last) };
};

遞歸終止模板類可以有多種寫法,比如上例的遞歸終止模板類還可以這樣寫:

template<typename... Args> struct sum;
template<typename First, typenameLast>
struct sum<First, Last>
{ 
  enum{ value = sizeof(First) +sizeof(Last) };
};

在展開到最后兩個(gè)參數(shù)時(shí)終止。還可以在展開到0個(gè)參數(shù)時(shí)終止:

template<>struct sum<> { enum{ value = 0 }; };

2.2.2繼承方式展開

還可以通過繼承方式來展開參數(shù)包,比如下面的例子就是通過繼承的方式去展開參數(shù)包:

//整型序列的定義
template<int...> struct IndexSeq {};

//繼承方式,開始展開參數(shù)包
template<int N, int... Indexes> struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...> {};

// 模板特化,終止展開參數(shù)包的條件
template<int... Indexes> struct MakeIndexes<0, Indexes...>
{
  typedef IndexSeq<Indexes...> type;
};

int main()
{
  using T = MakeIndexes<3>::type;
  cout << typeid(T).name() << endl;
  return 0;
}

其中MakeIndexes的作用是為了生成一個(gè)可變參數(shù)模板類的整數(shù)序列,最終輸出的類型是:struct IndexSeq<0,1,2>。

MakeIndexes繼承于自身的一個(gè)特化的模板類,這個(gè)特化的模板類同時(shí)也在展開參數(shù)包,這個(gè)展開過程是通過繼承發(fā)起的,直到遇到特化的終止條件展開過程才結(jié)束。MakeIndexes<1,2,3>::type的展開過程是這樣的:

MakeIndexes<3> : MakeIndexes<2, 2>{}
MakeIndexes<2, 2> : MakeIndexes<1, 1, 2>{}
MakeIndexes<1, 1, 2> : MakeIndexes<0, 0, 1, 2>
{
  typedef IndexSeq<0, 1, 2> type;
}

通過不斷的繼承遞歸調(diào)用,最終得到整型序列IndexSeq<0, 1, 2>。

如果不希望通過繼承方式去生成整形序列,則可以通過下面的方式生成。

template<int N, int... Indexes>
struct MakeIndexes3
{
  using type = typename MakeIndexes3<N - 1, N - 1, Indexes...>::type;
};

template<int... Indexes>
struct MakeIndexes3<0, Indexes...>
{
  typedef IndexSeq<Indexes...> type;
};

3.變參模板的應(yīng)用

我們可以利用遞歸以及偏特化等方法來展開模板參數(shù)包,那么實(shí)際當(dāng)中我們會(huì)怎么去使用它呢?我們可以用變參模板來消除一些重復(fù)的代碼以及實(shí)現(xiàn)一些高級(jí)功能,下面我們來看看可變參模板的一些應(yīng)用。

3.1消除重復(fù)代碼

C++11之前如果要寫一個(gè)泛化的工廠函數(shù),這個(gè)工廠函數(shù)能接受任意類型的入?yún)?,并且參?shù)個(gè)數(shù)要能滿足大部分的應(yīng)用需求的話,我們不得不定義很多重復(fù)的模版定義,比如下面的代碼:

template<typename T> T* Instance()
{
  return new T();
}

template<typename T, typename T0> T* Instance(T0 arg0)
{
  return new T(arg0);
}

template<typename T, typename T0, typename T1> T* Instance(T0 arg0, T1 arg1)
{
  return new T(arg0, arg1);
}

template<typename T, typename T0, typename T1, typename T2> 
T* Instance(T0 arg0, T1 arg1, T2 arg2)
{
  return new T(arg0, arg1, arg2);
}

struct A
{
  A(int){}
};

struct B
{
  B(int,double){}
};
A* pa = Instance<A>(1);
B* pb = Instance<B>(1,2);

可以看到這個(gè)泛型工廠函數(shù)存在大量的重復(fù)的模板定義,并且限定了模板參數(shù)。用可變模板參數(shù)可以消除重復(fù),同時(shí)去掉參數(shù)個(gè)數(shù)的限制,代碼很簡(jiǎn)潔, 通過可變參數(shù)模版優(yōu)化后的工廠函數(shù)如下:

template<typename T,typename... Args> T* Instance(Args&&... args)
{
  return new T(std::forward<Args>(args)...);
};
A* pa = Instance<A>(1);
B* pb = Instance<B>(1,2)

3.2實(shí)現(xiàn)泛化的delegate

C++中沒有類似C#的委托,我們可以借助可變模版參數(shù)來實(shí)現(xiàn)一個(gè)。C#中的委托的基本用法是這樣的:

delegate int AggregateDelegate(int x, int y);//聲明委托類型

int Add(int x, int y){return x+y;}
int Sub(int x, int y){return x-y;}

AggregateDelegate add = Add;
add(1,2);//調(diào)用委托對(duì)象求和
AggregateDelegate sub = Sub;
sub(2,1);// 調(diào)用委托對(duì)象相減

C#中的委托的使用需要先定義一個(gè)委托類型,這個(gè)委托類型不能泛化,即委托類型一旦聲明之后就不能再用來接受其它類型的函數(shù)了,比如這樣用:

int Fun(int x, int y, int z){return x+y+z;}
int Fun1(string s, string r){return s.Length+r.Length; }
AggregateDelegate fun = Fun; //編譯報(bào)錯(cuò),只能賦值相同類型的函數(shù)
AggregateDelegate fun1 = Fun1;//編譯報(bào)錯(cuò),參數(shù)類型不匹配

這里不能泛化的原因是聲明委托類型的時(shí)候就限定了參數(shù)類型和個(gè)數(shù),在C++11里不存在這個(gè)問題了,因?yàn)橛辛丝勺兡0鎱?shù),它就代表了任意類型和個(gè)數(shù)的參數(shù)了,下面讓我們來看一下如何實(shí)現(xiàn)一個(gè)功能更加泛化的C++版本的委托(這里為了簡(jiǎn)單起見只處理成員函數(shù)的情況,并且忽略const、volatile成員函數(shù)的處理)。

template <class T, class R, typename... Args>
class MyDelegate
{
public:
  MyDelegate(T* t, R(T::*f)(Args...)) :m_t(t), m_f(f) {}

  R operator()(Args&&... args)
  {
    return (m_t->*m_f)(std::forward<Args>(args) ...);
  }

private:
  T * m_t;
  R(T::*m_f)(Args...);
};

template <class T, class R, typename... Args>
MyDelegate<T, R, Args...> CreateDelegate(T* t, R (T::*f)(Args...))
{
  return MyDelegate<T, R, Args...>(t, f);
}

struct A
{
  void Fun(int i) { cout << i << endl; }
  void Fun1(int i, double j) { cout << i + j << endl; }
};

int main()
{
  A a;
  auto d = CreateDelegate(&a, &A::Fun);    //創(chuàng)建委托
  d(1);                    //調(diào)用委托,將輸出1
  auto d1 = CreateDelegate(&a, &A::Fun1);   //創(chuàng)建委托
  d1(1, 2.5);                 //調(diào)用委托,將輸出3.5
}

MyDelegate實(shí)現(xiàn)的關(guān)鍵是內(nèi)部定義了一個(gè)能接受任意類型和個(gè)數(shù)參數(shù)的“萬能函數(shù)”:R (T::*m_f)(Args…),正是由于可變模版參數(shù)的特性,所以我們才能夠讓這個(gè)m_f接受任意參數(shù)。

4.總結(jié)

使用變參模板能夠簡(jiǎn)化代碼,正確使用的關(guān)鍵是如何展開參數(shù)包,展開參數(shù)包的過程是很精妙的,體現(xiàn)了泛化之美、遞歸之美,正是因?yàn)樗哂猩衿娴摹澳ЯΑ?,所以我們可以更泛化的去處理問題,比如用它來消除重復(fù)的模版定義,用它來定義一個(gè)能接受任意參數(shù)的“萬能函數(shù)”等。其實(shí),可變模版參數(shù)的作用遠(yuǎn)不止文中列舉的那些作用,它還可以和其它C++11特性結(jié)合起來,比如type_traits、std::tuple等特性,發(fā)揮更加強(qiáng)大的威力。

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

相關(guān)文章

  • C語言入門之淺談數(shù)據(jù)類型和變量常量

    C語言入門之淺談數(shù)據(jù)類型和變量常量

    這篇文章主要為大家介紹了C語言數(shù)據(jù)類型和變量常量,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • C++中的string庫函數(shù)常見函數(shù)的作用和使用方法

    C++中的string庫函數(shù)常見函數(shù)的作用和使用方法

    這篇文章主要介紹了C++中的string庫函數(shù)常見函數(shù)的作用和使用方法,庫函數(shù)的靈活應(yīng)用是程序員的一大重要技能,本文通過實(shí)例實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • C語言中如何實(shí)現(xiàn)桶排序

    C語言中如何實(shí)現(xiàn)桶排序

    這篇文章主要介紹了C語言中如何實(shí)現(xiàn)桶排序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 深度理解c++中的this指針

    深度理解c++中的this指針

    這篇文章主要介紹了C++編程指向成員的指針以及this指針的基本使用指南,與C語言一樣,存儲(chǔ)的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址,需要的朋友可以參考下。
    2016-07-07
  • C++中的數(shù)組引用和指針引用

    C++中的數(shù)組引用和指針引用

    這篇文章主要介紹了C++中的數(shù)組引用和指針引用詳細(xì)的相關(guān)資料,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • VC定時(shí)器的用法實(shí)例詳解

    VC定時(shí)器的用法實(shí)例詳解

    這篇文章主要介紹了VC定時(shí)器的用法,以實(shí)例形式詳細(xì)講述了VC定時(shí)器的原理與具體用法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2014-10-10
  • c++非變易算法-stl算法

    c++非變易算法-stl算法

    本文主要介紹了C++ STL算法庫中的非變易算法,是一些原則上不會(huì)變更操作數(shù)據(jù)的算法,包括:逐個(gè)查找算法、元素搜索算法、元素統(tǒng)計(jì)算法、序列匹配算法、子序列搜索算法、這些函數(shù)均包含于<algorithm>頭文件,本文給出的所有代碼在VS2010中編譯運(yùn)行通過
    2014-03-03
  • vs2019+win10配置boost庫的詳細(xì)教程

    vs2019+win10配置boost庫的詳細(xì)教程

    這篇文章主要介紹了vs2019+win10配置boost庫,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 詳解計(jì)數(shù)排序算法及C語言程序中的實(shí)現(xiàn)

    詳解計(jì)數(shù)排序算法及C語言程序中的實(shí)現(xiàn)

    技術(shù)排序算法與我們普通接觸的冒泡排序和快速排序等基于元素比較的算法不同,在編程中通過C語言的數(shù)組能夠清除地表達(dá)出來,這里我們就來詳解計(jì)數(shù)排序算法及C語言程序中的實(shí)現(xiàn)
    2016-07-07
  • C++編程中使用設(shè)計(jì)模式中的policy策略模式的實(shí)例講解

    C++編程中使用設(shè)計(jì)模式中的policy策略模式的實(shí)例講解

    這篇文章主要介紹了C++編程中使用設(shè)計(jì)模式中的policy策略模式的實(shí)例講解,文章最后對(duì)策略模式的優(yōu)缺點(diǎn)有一個(gè)簡(jiǎn)單的總結(jié),需要的朋友可以參考下
    2016-03-03

最新評(píng)論

上林县| 金塔县| 化隆| 崇阳县| 石阡县| 华坪县| 黑河市| 汾阳市| 红河县| 太湖县| 商洛市| 新乡市| 张家港市| 五峰| 翁源县| 朝阳县| 松潘县| 房山区| 奉新县| 祁连县| 盐边县| 山东| 姜堰市| 汕尾市| 裕民县| 高淳县| 石渠县| 广南县| 同仁县| 寻甸| 长岭县| 漳州市| 微山县| 杂多县| 保亭| 泊头市| 宿迁市| 锦州市| 鸡东县| 宿州市| 新民市|