函數模板
函數模板概念
函數模板:編譯器生成代碼的一個規則。函數模板代表了一個函數家族,該函數模板與類型無關,在使用時被參數化,根據實參類型產生函數的特定類型版本。
函數模板格式
//要讓這個函數與類型無關
//Add函數模板
template<class T,typename s> //模板的參數列表
//告訴編譯器T是一種類型
T Add(T left, T right)
{return left + right;
}
函數模板原理
針對于下面的代碼,我們看編譯器給我們的反匯編代碼,來了解函數模板編譯器是怎么處理的?
T Add(T left, T right)
{cout << typeid(T).name() << endl;return left + right;
}
int main()
{//對函數模板實例化Add(1, 2);Add(1.0, 2.0);Add('1', '2');return 0;
}
每個函數地址不一樣,說明編譯器為每個類型都準備了相對應的函數
在編譯階段,如果編譯器檢測到對某個函數模板實例化
- 對實參類型進行推演(和函數重載有點像),根據推演的結果,確認模板參數列表中T的實際類型
- 將它集合函數模板,處理具體類型的函數,生成對應類型的函數
但是下面這個調用,就會報錯,因為第一參數給成整型,編譯器就會認為T是整型,但是第二個參數是double類型,那么編譯器又認為T是double類型,兩者沖突,編譯器就不知道T到底是什么。
模板一般不會進行相應類型轉化
Add(1,2.0)
解決方法:
T Add(T left, T2 right)
{cout << typeid(T).name() << endl;return left + right;
}
也可以手動進行強制類型轉換
也可以這樣調用Add<int>('1', '2.0');
函數模板的實例化
用不同類型的參數使用函數模板時,稱為函數模板的實例化。模板參數實例化分為:隱式實例化和顯示實例化
隱式實例化
讓編譯器根據實參推演模板參數的實際類型
T Add(T left, T right)
{cout << typeid(T).name() << endl;return left + right;
}
int main()
{//對函數模板實例化Add(1, 2);Add(1.0, 2.0);Add('1', '2');return 0;
}
顯示實例化
在函數名后的<>中指定模板參數的實際類型
如果類型不匹配,編譯器會嘗試進行隱式類型轉換,如果無法轉換成功編譯器將會報錯。
Add<int>('1', '2.0');
函數模板參數的匹配原則
- 一個非模板函數可以和一個同名的函數模板同時存在,而且該函數模板還可以被實例化為這個非模板函數
int Add(int left, int right)
{return right + left;
}
template<class T>
T Add(T left, T right)
{return left + right;
}int main()
{//同名函數與函數模板同時存在時,優先使用模板生成的函數Add<>(1, 2); //隱式實例化Add(1,2);return 0;
}
- 對于非模板函數和同名函數模板,如果其他條件都相同,在調動時會優先調用非模板函數而不會從該模板產生出一個實例。如果模板可以產生一個具有更好匹配的函數, 那么將選擇模板
int Add(int left, int right)
{return right + left;
}
template<class T>
T Add(T left, T right)
{return left + right;
}
int main()Add(1, 2); // 與非函數模板類型完全匹配,不需要函數模板實例化Add(1, 2.0); // 模板函數可以生成更加匹配的版本,編譯器根據實參生成更加匹配的Add函數return 0;
}
- 模板函數不允許自動類型轉換,但普通函數可以進行自動類型轉換
類模板
類模板的定義
typedef int DataType;
template<class T>
class SeqList
{
public:SeqList(size_t capacity = 10):_array(new T(capacity)), _size(0), _capacity(capacity){}~SeqList(){delete[] _array;_array = nullptr;_capacity = 0;_size = 0;}//尾部void PushBack(const T& data);void PopBack(){--_size;}//任意位置插入和刪除/*void Insert(size_t pos, T&data);void Erase(size_t pos);*///在const類型成員函數中不能修改成員變量//const修飾this指針size_t size()const{return _size;}size_t Capacity()const{return _capacity;}bool Empty()const{return 0 == _size;}T&operator[](size_t index){assert(index < _size);return _array[index];}const T&operator[](size_t index) const{assert(index < _size);return _array[index];}
private:void _CheckCapacity(){if (_size == _capacity){//開辟新空間T* array = new T[2 * _capacity];//拷貝元素//memcpy(array, this->_array, _size*sizeof(T));for (size_t i = 0; i < _size; ++i){array[i] = _array[i];}//釋放舊空間delete[] _array;_array = array;_capacity *= 2;}}
private:T* _array;size_t _size;size_t _capacity;
};
template<class T>
void SeqList<T>::PushBack(const T& data)
{_CheckCapacity();_array[_size] = data;_size++;
}
類模板的實例化
類模板實例化與函數模板實例化不同,類模板實例化需要在類模板名字后跟<>,然后將實例化的類型放在<>中即可,類模板名字不是真正的類,而實例化的結果才是真正的類。
void TestSeqList()
{SeqList<int> s1;s1.PushBack(1);s1.PushBack(2);s1.PushBack(3);s1.PushBack(4);cout << s1[2] << endl;s1[2] = 10;cout << s1.size() << endl;cout << s1.Capacity() << endl;cout << s1[2] << endl;SeqList<double> s2;
}