1. 程式人生 > >C++ 模板學習總結(三)模板引數的三種形式

C++ 模板學習總結(三)模板引數的三種形式

之前的一篇文章中介紹了特化和例項化的知識,那麼本次想給大家介紹一下模板的三種引數。

首先呢,要說的是什麼是模板引數,那麼如果使用過模板的同學會知道在定義一個模板的時候需要在定義上面寫上一行諸如template<typename T>這樣的程式碼,那麼這行中定義的這個T就是一個模板引數。對於一個模板而言,無論是函式模板還是類模板,都需要對其指定模板引數,可以有多個,但至少要有一個,如果一個都沒有那還叫什麼模板,當用戶在使用這個模板的時候需要指定所有的模板引數以生成一個可用的特化。注意,存在的每一個模板引數在正式使用的時候都必須是被指定的,雖然指定的方法可以不是顯式指定,但必須可以推匯出來,換句話說編譯器必須要能知道每個引數是什麼才行。那麼對於類模板而言,除了顯示指定所有模板引數之外,還可能是該引數有預設值,而對於函式模板顯然要複雜的多,模板的引數可以通過函式引數推導而來,這部分被稱為模板實參演繹,也是一個非常複雜的議題,我們留之後議。

首先要了解的是雖然我們最常見的模板引數都是型別引數,例如:

template<typename T>
class Test
{};


在這個簡單的示例中,T就是一個型別引數,它的意思是說T表示了一個型別,那麼在Test這個class中就可以按照使用一個型別的方式來使用T,比如可以使用T來宣告一個例項,或宣告一個指標型別,還可以通過它來做型別轉換等等,總之一個正常型別可以使用的地方就可以使用T,看看下面的例子
template<typename T>
class Test
{
public:
	T tObj;
	T* tPtr;
	std::list<T> l;
	T* createInsideObj();
};

此例中,我們使用T來建立一個物件還建立一個T的指標物件,除此之外,該類中的createInsideObj方法返回型別也被指定為T*,甚至還可以將T傳遞給其他的類模板。那麼當我們使用這個類模板Test的時候就需要指定T具體的型別到底是什麼,例如Test<int>就會將T替換為int,但是有一個原則是不能違背的,就是不能使用T去做它沒有定義的工作,例如:

template<typename T>
T* Test<T>::createInsideObj()
{
	return new T();
}

我們這裡給出了createInsideObj的實現,這個例子沒有任何實質意義只為說明語法,在定義中使用new T()來建立一個物件,那麼仔細觀察會發現,該定義要求T型別一定要有一個public的無參建構函式,例如我們使用Test<int>來例項化該類模板
int main()
{
	Test<int> a;
	int* p= a.createInsideObj();
}


這顯然是沒什麼問題的,但是如果我有一下一個類定義

class A
{
A(){}
};
該類A將無參建構函式設定為private的,那麼再來試試看,
int main()
{
	Test<A> a;
	int* p= a.createInsideObj();
}
首先,在Test<A> a這一行就會報錯,為啥呢,因為Test中定義了一個T型別的tObj,該成員就需要無參建構函式來初始化,而A的無參構造為私有,所以不行,對於第二句同樣也不行。那麼如果去掉這個tObj的定義,然後在main函式中不去呼叫createInsideObj這個函式就不會報錯了,因為在之前我們將例項化的時候講過按需編譯的概念。

整體上這個概念是比較簡單的,所以也就沒太多可以講解的,那麼除了這種最常見的型別引數還有其他兩種:

非型別模板引數(Non-type template parameters)

模板模板引數(Template template parameters)

除了以上兩種,在C++11中還引申出一種新的變長模板引數(Variadic Parameters)的概念,但由於它是基於以上三種的所以就不單獨歸類了。
我們先來看看非型別模板引數,這種其實和型別模板引數非常相似,不同的是它所指代的不是一個型別而是一個值,先舉個最常見的例子:

template<int N>
class Test
{
public:
	int array[N];
};


大家可以看到這個例子中的N就是一個非型別引數,其為int型,而且在Test中被作為一個常量對待。我們可以用一個int值來對其賦值,例如Test<100>,那麼此時N就被替換為100。當時在公司講解此處的時候一位同事提了一個問題說這玩意看起來沒啥用啊,為啥這個N值不直接指定呢,這個問題顯然是沒有理解到模板的作用,對於這個N是一個可變的值,除了此處見到的這個N用來指定陣列長度之外,在函式模板中還常常用來控制遞迴。這個後面遇到再說吧。

需要明白的是,不是所有的型別都可以來指定非型別引數,除了int之外還有其他一些可以被指定為非型別引數的型別,如下:

For integral For pointers toobjects For pointers tofunctions For lvalue referenceparameters For pointers tomembers enumeration type nullptr_t

對此就不一一說明只給出一個例子:

int ai[5];

template<const int* pci> struct X {};
X<ai> xi;  // ok: array to pointer conversion and cv-qualification conversion

struct Y {	void func(){}   };
struct  YY {     static Y y;    };
Y YY::y;

template<const Y* b> struct Z {};
Y y;
Z<&YY::y> z;  // ok: cv-qualification conversion
 
template<int (&pa)[5]> struct W {};
W<ai> w; // ok: no conversion

void f(char);           
void f(int);
template<void (*pf)(int)> struct RR {};
RR<&f> a; // ok: overload resolution selects f(int)

template<void (Y::*pf)()> struct RD {};
RD<&Y::func> rd; 


沒記錯的話,這個例子是來自cppreference的,另外需要說明的只有一點,int (&)[5] 這個型別可能很多人沒見過,此型別表示的是一個長度為5的int陣列引用型別,而int (&pa)[5]表示的是pa為一個引用物件。順便也說一下void (Y::*pf)()表示的是Y類中一個返回型別為void無參的成員函式指標,所以可以用&Y::func來對其賦值。個人覺得這個部分的概念相對還是比較簡單的,有一點需要說明的是可能有些型別的表示方式我們不太常見,例如T1(T2), T(&)[N]這種的,需要大家自己瞭解一下,其他的有一點要說明的是,對於指標型別和引用型別,所用來賦值的物件必須是有連結的,《C++ Templates》上說必須要有外部連結,我發現新的g++版本上面內部連結和外部連結都可以,老版本是必須外部連結。

剩下最後一種就是模板的模板引數,這名字念起來就有點拗口,意思就是說其模板引數還是一個模板,我感覺這個看起來比較複雜,其實非常簡單,直接來看看例子吧:

template<class T, int a> class A {};

template<template<typename, int> class V> class C
{
public:
    V<int, 5> y; // uses the primary template
};

int main()
{
    C<A> aa;
    return 0;
}

那麼類模板C的模板引數就是一個 模板模板引數了,使用上面沒覺得有什麼特別的,所以就不再過多解釋了,之後還剩下一個變長模板引數後續再講,今天先到這裡

相關推薦

C語言學習總結1-遞迴函式的理解

啥是遞迴? 即是該函式呼叫它本身自己,這種呼叫過程稱為遞迴。 遞迴可以相當於迴圈,所以想結束遞迴,就必須有終止遞迴的條件測試部分,否則就會出現無限遞迴(即無限迴圈)。同時,這也是使用遞迴的難點

人工智慧學習總結1——人工智慧的個分支:認知、機器學習、深度學習

人工智慧進入了一切領域——從自動駕駛汽車,到自動回覆電子郵件,再到智慧家居。 你似乎可以獲得任何商品(例如醫療健康,飛行,旅行等),並通過人工智慧的特殊應用使其更加智慧。所以除非你相信事件具有終結者般的轉折,你可能會問自己,人工智慧能夠預示著工作場所或整體的業務線的什麼利益。

C語言學習總結——C庫函式總結

C 庫函式主要指那些由美國國家標準協會(ANSI)或國際標準化組織(ISO)釋出的標準中規定的庫函式,按照標準 C 的要求來進行 C 語言程式設計是很重要的,因為這樣你的程式碼才有可能跨平臺使用。 最早的 C89 中有15個標準標頭檔案: asse

C++ 模板學習總結模板引數形式

之前的一篇文章中介紹了特化和例項化的知識,那麼本次想給大家介紹一下模板的三種引數。 首先呢,要說的是什麼是模板引數,那麼如果使用過模板的同學會知道在定義一個模板的時候需要在定義上面寫上一行諸如template<typename T>這樣的程式碼,那麼這行中定義的

c++學習總結——運算子過載與標準模板STL

一、心得總結     運算子過載使得使用者自定義的資料以一種更簡潔的方式工作。例如在做ATM模擬系統時,使用過載“<”來比較時間,可以簡化程式,減少程式碼。另外,我們也可以過載運算子函式,將運算子用於操作自定義的資料型別。過載運算子函式可以對運算子做出新的解釋,即定義使用

c++學習總結——虛擬函式與多型

一、學習總結     在面向物件程式設計中,多型性是指一個名字,多種語義;或者介面相同,多種實現。過載函式是多型性的一種簡單形式。C++為類體系提供一種靈活的多型機制——虛擬函式。虛擬函式允許函式呼叫與函式體的聯絡在執行時才進行,成為動態聯編。類、繼承和多型,提供了對軟體重用性

c++學習總結——繼承

一、心得體會     之前的程式碼在定義類時,如果需要多次用到某一個類,都需要反覆定義使用它,但繼承的學習卻彌補這一缺點。整合式面向物件程式設計中軟重用的關鍵技術。繼承機制使用已經定義的類作為基礎建立新的類定義,新的類時原有類的資料及操作與新類所增加的資料及操作的組合。新的類把

零基礎學習OpenGL--模板測試

       模板測試在深度測試之前。當片段著色器處理完一個片段後,模板測試會開始執行,被保留下來的才會進入深度測試。模板測試對應模板緩衝。        模板緩衝:每個模板值8位表示,這樣每個片段就有2的8次方,2

敏捷開發系列學習總結11——Scrum敏捷開發流程的個角色、四個會議和個物件

Scrum敏捷開發流程主要包擴三個角色、四個會議和個三物件。 三個角色 Scrum團隊中包括三個角色,他們分別是產品負責人、開發團隊和 專案的直接管理者(Scrum Master)。 Scrum 團隊是自組織、跨職能的完整團隊。自組織團隊決定如何最好地完成他們的工作

Docker學習總結41——個技巧,將Docker映象體積減小90%

一、前言 在構建Docker容器時,應該儘量想辦法獲得體積更小的映象,因為傳輸和部署體積較小的映象速度更快。但RUN語句總是會建立一個新層,而且在生成映象之前還需要使用很多中間檔案,在這種情況下,該如何獲得體積更小的映象呢?你可能已經注意到了,大多數Dockerfiles都

《深度探索C++物件模型》學習總結——前言與導讀

前言 Foundation專案:為了構建大系統而努力定義的一個新的開發模型。 ALF:一種一面物件層次結構,提供一個永久的、以語意為基礎的表現法。 Simplifier的工作:轉換內部的程式表現。 任何物件模型都需要的轉換風味(?): 1. 與

Docker學習總結3——Docker實戰之入門以及Dockerfile

應用映象 csphere/wordpress:4.2 # cd docker-training/wordpress/ # ls -a . license.txt wp-config-sample.php wp-login.

spring boot 學習筆記 4模板引擎 Thymeleaf

Thymeleaf 是⾯向 Web 和獨⽴環境的現代伺服器端 Java 模板引擎,能夠處理 HTML、XML、JavaScript、CSS 甚⾄純⽂本。 Thymeleaf 特點 簡單說,Thymeleaf 是一個跟 Velocity、FreeMarker 類似的模板引擎,它可以完全替代 J

Django 學習筆記模板變數

關於Django模板變數官方網址:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.傳入普通變數 在hello/Hello World/temlplates/index.html中,修改html檔案 <!DOCTYPE h

Django 學習筆記模板標籤

關於Django模板標籤官方網址https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.IF標籤 Hello World/views.py 1 from django.shortcuts import render 2 3

C/C++學習總結複習

C/C++學習了太久,有些知識點有些模糊了,花了半個多月,重新整理了一些,自認為比較重要的知識點,主要是用於自己學習。 1.volatile優化總結: volatile 影響編譯器編譯的結果,指出,volatile 變數是隨時可能發生變化的,與volatile

C/C++日常學習總結第一篇const用法及printf的執行順序

1.c語言中printf在不同編譯器下面的執行順序    【程式碼】:   int n = 0; printf("%d,%d,%d",++(++n),++(++n),++(++n));    【結果】:      VC6.0下面的結果是:6,5,4        

flask學習筆記--模板中使用url_for

歡迎加入知了課堂,學習flask之前在檢視函式中使用url_for,實現從檢視函式中跳轉到另一個url。現在我為大家演示如何在模板中使用url_for,實現在頁面點選文字,跳轉至另一個頁面。其實很簡單一、方法1.首先通過檢視函式,渲染出一個頁面@app.route('/')d

設計模式學習總結策略模式(Strategy)

isp 筆記本 override div ont 角色 write stat 通過   策略模式,主要是針對不同的情況采用不同的處理方式。如商場的打折季,不同種類的商品的打折幅度不一,所以針對不同的商品我們就要采用不同的計算方式即策略來進行處理。   一、示例展示:   以

設計模式學習總結適配器模式(Adapter)

實現接口 國外 手機 額外 sed ges program ebe 通過   適配器模式主要是通過適配器來實現接口的統一,如要實現國內手機在國外充電,則需要在不同的國家采用不同的適配器來進行兼容!   一、示例展示:   以下例子主要通過給筆記本電腦添加類似手機打電話和發短