標準模板庫

標準模板庫（Standard Template Library，縮寫：STL），是一個C++軟體庫，大量影響了C++標準程序庫但並非是其的一部分。其中包含4個組件，分別為演算法、容器、函數、迭代器。

模板是C++程序設計語言中的一個重要特徵，而標準模板庫正是基於此特徵。標準模板庫使得C++程式語言在有了同Java一樣強大的類庫的同時，保有了更大的可擴展性。

## 歷史

標準模板庫係由亞歷山大·斯特潘諾夫創造於1979年前後，這也正是比雅尼·斯特勞斯特魯普創造C++的年代。

雖然David R. Musser於1971年開始即在電腦幾何領域發展並倡導某些泛型程序設計觀念，但早期並沒有任何程式語言支援泛型程序設計。Alex和Musser曾於1987開發出一套相關的Ada library.

標準模板庫設計人Stepanov早期從事教育工作，1970年代研究泛型程序設計，那時他與其同事一起在GE公司開發出一個新的程序語言——Tecton。

1983年，Stepanov先生轉至紐約大學坦登工程學院擔任助理教授，繼續研究泛型程序設計，同時寫了許多Scheme的程序，應用在graph與network的演演算法上，1985年又轉至GE公司專門教授高階程序設計，並將graph與network的Scheme程式，改用Ada寫，用了Ada以後，他發現到一個動態（dynamically）類型的程序（如Scheme）與強制（strongly）類型的程序（如Ada）有多麼的不同。

在動態類型的程序中，所有類型都可以自由的轉換成別的類型，而強制類型的程序卻不能。但是，強制類型在出錯時較容易發現程序錯誤。

1988年Stepanov先生轉至HP公司執行開發泛型程序庫的工作。此時，他已經認識C語言中指標(pointer)的威力，他表示一個程式設計師只要有些許硬體知識，就很容易接受C語言中指標的觀念，同時也瞭解到C語言的所有數據結構均可以指標間接表示，這點是C與Ada、Scheme的最大不同。

Stepanov並認為，雖然C++中的繼承功能可以表示泛型設計，但終究有個限制。雖然可以在基礎類型（superclass）定義演算法和接口，但不可能要求所有物件皆是繼承這些，而且龐大的繼承體系將減低虛擬（virtual）函數的執行效率，這便違反的前面所說的「效率」原則。

到了C++模板觀念，Stepanov參加了許多有關的研討會，與C++之父比雅尼討論模板的設計細節。如，Stepanov認為C++的函數模板（function template）應該像Ada一樣，在聲明其函數原型後，應該顯式的聲明一個函數模板之實例（instance）；比雅尼則不然，他認為可以透過C++的重載（overloading）功能來表達。

Stepanov想像中的函數模板：

```cpp
   //in *.hpp
   template<class T>
   T square(T x) { return x*x; }
 
   //in *.cpp
   double square(double);
   cout << square(3.3);
   int square(int);
   cout << square(3);
```

比雅尼認為的函數模板：

```cpp
   //in *.hpp
   template<class T>
   T square(T x) { return x*x; }
 
   //in *.cpp
   cout << square(3.3);
   cout << square(3);
```

幾經爭辯，Stepanov發現比雅尼是對的（參考侯俊傑〈標準模板庫講座·第三章〉）。事後Stepanov回想起來非常同意比雅尼的作法。

template 引數推導機制（arguments deduction），在標準模板庫中佔非常重要的角色。Alexander Stepanov（標準模板庫創造者）在與Dr. Dobb's Journal進行的訪談中說道『1992 我重回generic-library的開發工作。這時候C++有了template 我發現比雅尼完成了一個非常美妙的設計。之前我在Bell Lab曾參與數次模板的相關設計討論，並且非常粗暴地要求比雅尼應該將C++模板設計得儘可能像Ada generics那樣。我想由於我的爭辯是如此地粗暴，他決定反對。我瞭解到在C++中除了擁有類模板（class template）之外還擁有函數模板的重要性。然而我認為函數模板應該像Ada generics一樣，也就是說它們應該是顯式實例，比雅尼沒有聽進我的話，他設計了一個函數模板機制，其中的『模板』是以一個重載化機制（overloading mechanism）來進行隱式實例這項特殊的技術對我的工作具有關鍵性的影響，因為我發現它使我得以完成Ada不可能完成的許多動作。我非常高興比雅尼當初沒有聽我的意見。』（DDJ 1995 年三月號）

事實上，C++的模板，本身即是一套複雜的巨集語言（macro language），巨集語言最大的特色為：所有工作在編譯時期就已完成。顯式的聲明函數模板之實例，與直接透過C++的多載功能隱式聲明，結果一樣，並無很大區別，只是前者加重程式設計師的負擔，使得程式變得累贅。

1992年Meng Lee加入Alex的專案，成為另一位主要貢獻者。

1992年，HP泛型程序庫計畫結束，小組解散，只剩下Stepanov先生與Meng Lee小姐（她是東方人，標準模板庫的英文名稱其實是取STepanov與Lee而來），Lee先前研究的是編譯器的製作，對C++的模板很熟，第一版的標準模板庫中許多程式都是Lee的傑作。

1993年，Andy Koenig到史丹佛大學演講，Stepanov便向他介紹標準模板庫，Koenig聽後，隨即邀請Stepanov參加1993年11月的ANSI/ISO C++標準化會議，並發表演講。

Bell實驗室的Andrew Koenig於1993年知道標準模板庫研究計劃後，邀請Alex於是年11月的ANSI/ISO C++標準委員會會議上展示其觀念。並獲得與會者熱烈的迴應。

1994年1月6日，Koenig寄封電子郵件給Stepanov，表示如果Stepanov願意將標準模板庫的說明文件撰寫齊全，在1月25日前提出，便可能成為標準C++的一部份。Stepanov回信道："Andy, are you crazy?"。Koenig便說："Well, yes I am crazy, but why not try it?"。

Alex於是在次年夏天在滑鐵盧舉行的會議前完成其正式的提案，並以百分之八十壓倒性多數，一舉讓這個巨大的計劃成為C++ Standard的一部份。

標準模板庫於1994年2月年正式成為ANSI/ISO C++的一部份，它的出現，促使C++程式設計師的思維方式更朝向泛型編程（generic program）發展。

## 內容

STL 將「在數據上執行的操作」與「要執行操作的數據分開」，分別以如下概念指代：

- 容器：包含、放置數據的地方。
- 迭代器：在容器中指出一個位置、或成對使用以劃定一個區域，用來限定操作所涉及到的數據範圍。
- 演算法：要執行的操作。

### 容器

標準模板庫包含了序列容器（sequence containers）與關聯容器（associative containers）。

資料容器｜描述

序列容器 - 有序集

vector｜動態數組，兼容C語言陣列。vector可以如同陣列一樣的存取方式，例如使用下標（operator）運算子，並記得自己的長度資訊（size），您也可以使用物件的方式來存取vector（push_back、pop_back）。使用vector可以輕易地定義多維可調整型陣列（std::vector >）。要使用vector，必須含入vector表頭檔。vector可在O(1)內完成在末尾插入 / 移除元素，但在vector中間或開頭插入/移除元素，則需要消耗O(n)時間。

list｜list容器是一個有序（Ordered）的資料結構（循序容器），每個元素中存儲著上一個元素和下一個元素的地址（指針），因此是一個雙向連結的鍊表。與vector相比，其元素的訪問速度較慢，而在已知元素位置的情況下，插入和刪除速度較快。STL容器中唯一支持事務語義。

forward_list（單向鍊表）｜list的單鍊表版，去掉了一些操作。

deque（雙端隊列）｜可看做為能在常量時間內完成向開頭或結尾插入或刪除元素的vector，但是修改之後，其迭代器的有效性就無法得到保障。

array｜只能在初始化時指定大小的陣列，可視為內置陣列的封裝。

關聯容器 - 無序集

set｜不重複元素的集合。

multiset｜跟set具有相同功能，但允許重複的元素。

map｜關聯數組，每個元素含有兩個數據項，map將一個數據項映射到另一個數據項中。

multimap｜跟map具有相同功能，但允許重複的鍵值。

unordered_setunordered_multisetunordered_mapunordered_multimap｜分別類似於集合、多重集合、映射、多重映射，但使用哈希表實現。它的鍵（Keys）沒有排序（operator<），但是必須存在一個從鍵類型到size_t的哈希函數、且要求鍵之間可以判等（operator==）。自C++11起進入語言標準。

其他類型的容器

bitset｜存儲系列位類似的固定大小的布爾向量。實現按位運算，沒有迭代器，不是序列。可視為std::array。若需要改變序列長度，可用std::vector。

valarray｜數值類型的std::vector。犧牲泛型能力而專為數值計算做了優化，例如在陣列上的sin操作可對數組內所有數值取正弦。有些實現會對std::valarray應用向量指令等優化手段。一個觀點是裡面全是數值類型的valarray才是數學意義上的向量，而可以泛型的vector更該叫array——程式語言中的陣列。

### 迭代器

迭代器是泛化的指針，通過使用迭代器，開發者可以操作數據結構而無需關心其內部實現。根據迭代器的操作方式的不同，迭代器分為五種：

- 輸入迭代器
- 輸出迭代器
- 前向迭代器
- 雙向迭代器
- 隨機訪問迭代器

### 演算法

STL提供了一些常見 的演算法，如排序和搜索等。這些演算法與數據結構的實現進行了分離。因此，也可對自定義的數據結構使用這些演算法，只需讓這些自定義的數據結構擁有演算法所預期的迭代器。

### 函數對象

狹義的函數對象即重載了操作符的類的實例，而廣義來講所有可用 x(...) 形式調用的 x 都可稱為函數對象、或曰可調用對象。

### 適配器（Adaptor）

適配器為一個模板類，用於提供接口映射。

## 與C++標準程式庫的差異

一個常見的誤解是STL是C++標準程式庫的一部分，但事實上並非如此。例如hash table的資料結構實作在STL中有模板可供調用，但C++標準程式庫一直到C++11才加入了。參見無序關聯容器 (STL)。

## 外部連結

- C/C++ reference includes a section on the STL
- STL programmer's guide official guide from SGI
- Bjarne Stroustrup on The emergence of the STL (Page 5, Section 3.1)
- Apache stdcxx portable Open Source implementation based on Rogue Wave STL
- STLport multiplatform STL implementation
- Dinkumware commercial STL implementation (ships with Visual C++ and several other compilers)
- Rogue Wave STL Class Reference
- MPTL shared-memory parallel extension of the STL using the Native POSIX Thread Library。
- STXXL: an STL implementation for external memory.
- STLSoft libraries：open-source, 100% header-only, C/C++ libraries of technology-specific facades and STL extensions.