給小學六年級的簡易模板元「編程」入門-基礎編程

2021-02-20 方形輪子製造廠
前言,本系列是一個模板「編程」入門類文章,注意是編程,不涉及模板的基礎語法和泛型相關的內容。並且本位內容比較淺顯,旨在提供一個利用模板進行編程的通用思路,進階的內容我會給一些參考資料一起學習交流。

本文按計劃分為上下兩章,分別是基礎編程和技巧。基礎編程介紹了如何用模板做一些簡單的編程。技巧部分會說一些模板元編程的技法。

程式語言和編譯時

一個簡單的程式語言通常由什麼要素組成?這個問題很好回答,那就是數據和流程。其中我們把函數為fisrt class的程式語言的函數也叫做數據。

而總所周知,模板是一個圖靈完備的編譯時語言,那麼編譯時的數據有什麼呢?

我們看一個非常簡單的模板。

template<class T>
struct S1 {
using type = T;
};

S1<int>;

這個模板很簡單, 我們想想他幹了什麼。比如說我們用 S1<int> , 他接受一個int類型作為參數,返回一個int類型。在念一遍, 他接受一個int類型作為參數,返回一個int類型,有點像什麼?對, 函數。

此處我們假設返回值是結構體裡的欄位type方便描述

參數是一個類型,返回值是一個類型的函數。我們可以這麼寫這個模板

S1 = (T)=>{return T};

S1(int) 和 S1<int> 基本就一回事了。

這是這個程式語言最基本的東西--函數。那麼很自然的 這裡面的類型就是數據了。

而且總所周知的是,模板除了可以用類型為參數,他還接受包括 常量整數值(包括枚舉)、指向對象/函數/成員的指針、指向對象或函數的lvalue引用,或者std::nullptr_t (nullptr的類型)在內的這些編譯時知道是什麼的類型。

為什麼不接受列數據的指針?因為指針在編譯的時候需要連結才能知道是啥。而函數對象啥的都定義在頭文件了

template<int V>
struct S2 {
int value = V + 1;
};

比如這麼一個模板 他就是接受一個整數V返回一個value為V+1的函數。

S2 = (T)=>{return V+1};

而且令人感動的是,模板編程裡的函數是一個一等公民。也就是說模板也可以作為數據。不過這裡不可以作為返回值。

template<typename T1, template<typename> class C>
struct S3
{
using type = C<T1>;
};
S3<int, S1>;

這個就是一個接受一個一個參數的模板為參數,返回一個S3的type

S3 = (T1,C)=>{return C(T1)};
S3(int, S1)

總的來說, 模板就是的函數,接受一個編譯時可計算數據返回編譯時計算結果數據的函數。其實編譯時可計算數據是 類型, 編譯時無需連結常量, 編譯時可計算函數。

額外的,我們可以注意一下constexpr關鍵字。可以用這個聲明一個編譯時可以計算的數據和函數。可以理解成一個不能輸入輸出類型的模板函數。

編譯時的流程控制

我們現在有了數據了,我們現在就可以考慮完成流程控制了。來讓我們大聲說出三大流程:順序,分支,循環。

順序流程沒什麼可以說的就是一個模板編譯展開的過程。

重點是分支流程:

分支流程

編譯時分支流程, 最大的用處就是根據不同的類型執行不同的操作。是不聽得又有點耳熟?對,重載就是一個最常用的編譯時分支。

我們其實可以把這個操作放到運行時, 類似python很多時候就這麼幹。

def f(a):
if type(a)==str:
xxxx
elif type(a)==int:
yyyyy

這樣做,最大的問題是程序有運行時開銷。在C++中我們可以用重載

void f(int a){
xxxx
}

void f(float b){
xxxx
}

其實這就是編譯時的一種if。類似的我們還有倆個大招 偏特化 和 SFINAE.

先說偏特化, 我們把模板展開的過程叫做特化的話, 偏特化就是特化一部分。我們知道的是,比如我說一個重載做不了的事情。類型T做一件事,類型T*做另一個事情。重載就不能很好的做這件事。模板只要:

template<class T>
struct S1 {
using type = T;
};

template<class T>
struct S1<T*> {
using type = T;
};

這句話的意思就是, 如果類型T是指針,那麼type就是去掉指針類型的T,否則type就是T, 寫成函數就是。

S1=(T)=>{
if(T是指針){
return T去除指針;
}
return T
}

為什麼指針類型能匹配到特化而不是什麼都能匹配的T呢?這就是特化的時候會選擇最合適的那個的原則。很符合直覺。

你看到這個往往會覺得眼熟, 如果你看過 《STL源碼剖析》,你會很熟悉。對就是type trait. 這個有些不太好理解的東西。其實他就是利用偏特化做一些進行了if判斷而已。

SFINAE之前的文章講過

模板編程入門-sfinae基礎

,這裡再簡單說說。

這個機制的核心就是,編譯器在嘗試展開一個模板,如果失敗了,不會報錯,會嘗試展開下一個可能的展開。

我們可以用他做什麼偏特化做不了的事情呢?舉個例子,在編譯時判斷一個類型是否有某個成員。這個用偏特化是做不了的。舉個例子

template <typename T>
void f(typename T::foo) {
xxx
}

template <typename T>
void f( T) {
yyy
}

這個會展開第一個, 如果這時候T沒有foo成員 他會展開失敗, 類型檢查過不了。但是因為SFINAE機制的存在,他不會報錯,而是去展開第二個, 寫成函數就是

f =(T)=>{
if(T有foo){
return xxx;
}
return yyy;
}

淺顯易懂。當然,步入了C++20 我們有了更好的工具來完成這個這個任務,那就是約束概念, 這裡給出參考資料供讀者自行了解

https://zh.cppreference.com/w/cpp/language/constraints

除了這個, 還有個寶貝不能忽略。那就是 if constexpr。

這個可以根據一個編譯時可計算數據來進行編譯時的流程控制。

template <int V>
void f() {
if constexpr(v){
xxxx
}
}

這個就是符合你直覺的if, 只不過發生在了編譯時。

循環流程

有了if, 我們就想搞個for。說起了函數,我們很自然的想到了一個東西,對的,遞歸。通過遞歸我們可以輕而易舉實現for.

template <int V>
constexpr int r = V + r<V-1>;

template<>
constexpr int r<0> = 0;

翻譯程函數就是

r =(V)=>{
if (v==0){
return 0
}
return f(V-1)+V;
}

顯而易見。

知道了這個原理, 我們有一個令人開心的語法特性,可變參數模板,參考連結

https://zh.cppreference.com/w/cpp/language/parameter_pack

這個東西可以做很多事情。我們現在先知道這個東西是模板元編程編譯時數據的數組就行。

綜合 實現list

我們實現一個簡單的編譯時list, 提供一個按下標訪問。

先想想函數:

list =(T)=>{
if(is_empty(T)){
return {value:T[0], left:null}
}
returm {value:T[0], left:list(T[1:])}
}

value表示當前下標的值,left表示剩下的數組, 函數寫出了很容易, 我們翻譯成模板就行了, 不過因為模板不能空展開, 我們需要一個東西標誌一下null

struct list_null {};

我們隨便定義一個類型就行。然後我們實現這個函數:

template <typename... T>
struct list;


template<typename U,typename... T>
struct list<U,T... > {
using value = U;
using left = list<T...>;
};

template<typename U>
struct list<U> {
using value = U;
using left = list_null;
};

使用的時候,只要

using myl = list<int,int,int,int,float>;
myl::value;
myl::left::value;

即可。

很好 我們可以實現下標訪問了。

先用函數:

list_at =(int a, list T)=>{
if (a==0){
return T::value;
}
return list_at(a-1, T[1:])
}

遞歸, 直到為0的時候返回值就可以了, 思路很簡單, 寫成模板

template<int a, typename T>
struct list_at {
using value = typename list_at<a-1, typename T::left>::value;
};

template<typename T>
struct list_at<0, T> {
using value = typename T::value;
};

使用的時候,只要:

using myl = list<int,double,float,int,float>;
using t = typename list_at<2,myl>::value;

t就是數組存的類型。

綜上所述, 我們進行模板元編程的過程中,只要想出來用函數怎麼寫,然後翻譯成模板就可以了。所以我個菜雞猜測一下, 個人函數式編程的抽象能力決定了模板元編程的水平。

參考文獻

因為本人水平低下 所以最好多看看參考資料

[1]. https://zh.cppreference.com/w/cpp/language/templates cpprefence的模板章節

[2]. 現代 C++30講: https://time.geekbang.org/column/intro/256?code=TjUT9y8QEechQ9EIIAVu9Kilsx5u1FrzLLQaF8n3X8A%3D

[3]. 知乎文章 C++ 模板元編程:一種屠龍之技 https://zhuanlan.zhihu.com/p/137853957

[4]. github的 CppTemplateTutorial https://github.com/wuye9036/CppTemplateTutorial

如有勘誤或不懂得地方,點擊原文。

相關焦點

  • 少兒編程熱 家長最好冷處理:入門學習足夠,競賽之路難堅持
    市民李女士在給小學一年級的孩子制定暑期計劃的時候犯了難:「扎克伯格的寶寶都在學」的少兒編程班,報了怕孩子學雜了、學重複了;不報怕孩子輸在「起跑線」上。編程要不要學?什麼時候學才合適?   入門級學習,沒必要給孩子報培訓班   近日,記者以幼升小學生家長的身份探訪了南京一家比較有名的連鎖編程培訓機構。
  • Python編程入門書籍分享
    書本簡介零基礎:專注於入門,層層進階,幫助讀者輕鬆掌握Python基礎知識;通俗易懂:輕鬆幽默,毫無做作、晦澀之感,讓編程不枯燥、不乏味;深入淺出:指導讀者理解Python編程思想,領略Python魅力
  • 方程求解器-Scratch編程與五年級數學5
    學習目標 趣味Scratch青少兒編程開啟一個新的篇章模塊:Scratch編程與小學數學,目的是讓孩子在學習編程的同時,能融入課堂學習的數學知識,編程的同時增強數學興趣。本部分的程序以基礎編程為主,不會涉及複雜的編程,學生容易上手,同時能用編程展示課堂學習的數學知識。家長可以通過Scratch編程給孩子展示更加形象生動的數學知識,培養孩子的編程和數學興趣。學生可以在學習編程的同時融入課堂的數學知識,拓展自己的思維,活用所學的編程和數學知識。
  • 《零基礎同時學Scratch3.0與C++算法編程》
    、課程價格:3800元(2019.7.3日前),白菜價,謝絕議價;六、按照小學信息學奧賽設計,共收錄真實編程案例220+,信息奧賽真題100+;七、全套118節課程包教包會,從零講起,一個暑假讓學生從零到算法精通,涉及:歷屆信息學競賽真題,
  • 禮嘉實驗小學:一節從一年級開始的編程課
    2019年9月,5個班級,200餘名學生在禮嘉實驗小學正式入學,而將伴隨他們六年的編程課,也悄然的來到他們身旁。無電腦化編程 循序漸進培養網際網路意識「同學們,我們來回顧一下,什麼是『開始』編程寶寶?什麼是『結束』編程寶寶?」「兩個循環寶寶可不可以單獨使用?」
  • 提高編程質量和效率使用UG模板技巧
    大家好,我是橘子,今天給大家分享的是關於UG編程實際案例.喜歡我分享的文章或者還需要別的學習資料可以關注我私信UG都可以免費領取提高編程質量和效率使用UG模板技巧 通過建立UG編程模板的方式,以提高我們用ug編程的工作質量和效率,並
  • 少兒學python編程:小學幾年級開始學編程?學哪個程式語言合適?
    大家好,歡迎學習python,本文從基礎開始,會連續寫作,喜歡的朋友可以收藏一下,在某一天用到的時候可以回來看一看。思想決定行動。看某一件事情是否需要去做,首先,要從思想上高度重視,並且從內心願意為之付出和努力。今天就來談談為什麼要學習PYthon,建議從幾歲開始學習。
  • 零基礎的小白應該怎麼入門學習編程
    C語言入門門檻較高,學習難度相對較大,但許多語言都受到C的影響,基本概念的東西理念通用,學會了C能打下紮實的編程基礎,也降低了以後學習其他語言的難度,總之學習C語言是先苦後甜,剛開始要對自己狠心。不選Python的原因:容易造成基礎差,到後面會有很多基礎底層的概念混亂。 3、怎麼學編程是操作性很強的一門知識,看書少不了,但只有學習和實踐相結合才能起到很好的效果,一種學習方法是看書->研究書中例子->自己做些東西->網上找資料->看書。
  • 傲夢:孩子為什麼需要學習少兒編程?入門編程軟體選哪個比較好?
    在大部分家長的認知中,編程不是「必須」的學習課程。它的重要程度跟數學、英語等科目相比,差的太多,有的家長甚至覺得編程還不如舞蹈、美術重要。但其實學習編程的好處非常多,尤其這兩年颳起了少兒編程風。今天,傲夢君就給大家講講,孩子為什麼需要學習少兒編程,以及入門編程軟體選哪個比較好。孩子為什麼需要學習少兒編程?
  • 從Scratch開始零基礎編程學習、用Python入門
    有人朋友以前沒有任何的編程基礎,但是也想通過編程來實現自己的一些想法,做些工具解決自己的工具。那麼如何從零開始學習編程呢?筆者整理了下自己總結的回答資料,所以就有這篇文章。零基礎學習編程的,建議先找一門可見既所得的交互圖像式程式語言開始,拖拖控制項,雙擊進去補充寫幾行邏輯就行。比如我學編程開始就是已VB,Delphi入門的。
  • PLC編程入門基礎技術知識
    PLC是微機技術與傳統的繼電接觸控制技術相結合的產物,它克服了繼電接觸控制系統中的機械觸點的接線複雜、可靠性低、功耗高、通用性和靈活性差的缺點,充分利用了微處理器的優點,又照顧到現場電氣操作維修人員的技能與習慣,特別是PLC的程序編制,不需要專門的計算機程式語言知識,而是採用了一套以繼電器梯形圖為基礎的簡單指令形式,使用戶程序編制形象、直觀、方便易學;調試與查錯也都很方便。
  • 淺談 C++ 元編程
    元編程作為一種新興的編程方式,受到了越來越多的廣泛關注。結合已有文獻和個人實踐,對有關 C++ 元編程進行了系統的分析。受限於 C++ 對模板本身的限制,Andrei Alexandrescu 等人又發明了 D 語言,把元編程提升為語言自身的一個特性。元編程已被廣泛的應用於現代 C++ 的程序設計中。由於元編程不同於一般的編程,在程序設計上更具有挑戰性,所以受到了許多學者和工程師的廣泛關注。1.4 元編程的語言支持C++ 的元編程主要依賴於語言提供的模板機制。
  • Python零基礎入門在線課程 | Crossin的編程教室出品
    希望通過我們的努力,可以幫助你在編程學習的過程中節省時間、少走彎路。課程內容目前開放的課程有2門:1. Python零基礎入門入門課程面向沒有編程基礎或剛剛接觸編程的學習者。除課程內容外,還需要通過練習來鞏固所學的編程知識。對於新手,建議先按照課程列表依次學習,至少完成前八章,掌握 Python 的編程基礎。我們設定了一些項目選題,作為課程的中期和完結考察。
  • 計算機編程雙語班開放,讓編程和英文齊飛
    在學習編程的過程中,孩子化具體為抽象,將數據邏輯歸納為計算機可以理解的語言,進而動手實踐解決問題。④領先掌握下一門必備的通用語言編程將會像閱讀和寫作一樣成為孩子最基本的能力之一。隨著技術的發展,特別是智能時代的到來,編程會成為下一個「通用語言」。
  • Python遊戲編程
    課程要求授課對象:小學4年級~高中本課程不講解python語法的基礎知識,因此,本課程要求學生具備一定的python編程基礎。變量與全局變量,表達式的計算流程控制語句如 if,switch,for,while,break,continue函數的定義與使用授課教師王梓楠,天津大學計算機專業,10年IT軟體工程師背景,2019年開始從事信息學奧賽培訓,主講C++編程基礎
  • 想學編程,但是沒有基礎怎麼辦?
    如果想作為專業的程式設計師,那必須經過系統的學習,學習各種程式語言和計算機原理等等。但是普通老百姓想學個編程玩玩,想把自己的想法實現,可以向周圍的朋友吹吹牛,這個時候怎麼辦。有人說我看到C語言的代碼,腦袋就大,完全沒辦法往下學,有沒有啥程式語言能零基礎入門的,目前python算是比較好入門的程式語言,但是對於一些新手還是挺難的。有沒有更簡單的程式語言?有,就是圖像化編程,積木式編程,你只要按照你自己的想法把積木拖到一起就能實現一些簡單的功能。
  • 工業機器人編程入門_工業機器人的編程要求
    打開APP 工業機器人編程入門_工業機器人的編程要求 發表於 2019-10-15 14:41:45   工業機器人編程入門   工業機器人編程怎麼入門呢?
  • 西門子PLC編程入門基礎,西門子PLC怎麼學
    編程?模擬量?還是指令?接線?很多人暈頭轉向。今天咱們就先說說西門子PLC該怎麼學,將眾多知識點匯成一個合理有序的學習框架體系會讓你融會貫通,事半功倍。之前也有文章講過,學PLC的人無非兩種,有基礎、無基礎。
  • 更直觀地編程!ABB 機器人推出Wizard 簡易編程軟體
    abb最新推出的Wizard簡易編程軟體使機器人更易安裝、編程和操作,無需專門培訓,減少了首次使用機器人的用戶實現自動化的障礙!   Wizard簡易編程是一種圖形化編程方法,旨在使用戶能夠為ABB單臂YuMi協作機器人快速創建應用程式,而不需要專門的培訓。先來看視頻!  該簡易編程軟體基於Blockly概念建立。
  • 零基礎小白學編程,該如何入門?
    編程作為當下最熱門的職業,頗受年輕人的喜愛,很多人因為其高薪資,職業前景好的特點,紛紛選擇轉行加入編程的行列,但是,對於一個對編程知之甚少的小白來說,想要入門,應該從哪一步先開始,才能少走彎路呢?入門先從C語言入手小編認為,學習編程的第一步肯定是掌握一門程式語言。其實,大部分程式語言都十分相似,比如C++、C#、Java等都是從C語言一步步擴展得到的,因此,只要掌握C語言,其他的就可以觸類旁通。