各類關鍵字

俊king發表於2024-04-13

namespace

測試程式使用namespace包裹

示例程式碼:

#include <iostream>
#include <memory>
#include <list>
namespace jj01
{
void test_member_template() {}
}

namespace jj02
{
template<typename T>
using Lst = list<T, allocator<T>>;
void test_template_template_param() {}
}

int main(int argc, char** argv)
{
jj01::test_member_template();
jj02::test_template_template_param();
}

類别範本

可以允許使用者任意指定的部分可以抽出成為模板

template<typename T>
// 使用的時候在指明型別
class complex
{
public:
complex (T r = 0. T i = 0) : re(r), im(i) {}
complex& operator += (const complex&);
T real() const {return re;}
T imag() const {return im;}
private:
T re, im;s
}

函式模板

語法和類别範本一樣

template<typename T>
inline
const T& min(const T& a, const T& b) {return b < a ? b : a;}
// 使用的時候不需要指名型別 -> <符號的本質是運算子過載
stone r1(2, 3), r2(3, 3);
r3 = min(r1, r2);

成員模板

模板裡面的member,該member本身又是一個template -> 模板內的模板 -> 主要用來設計建構函式

示例程式碼:

template<class T1, class T2>
struct pair
{
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& a, const T2& b) : first(a), second(b) {}

// 再設計模板
template<class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) { }
};

模板特化

專門針對某一型別設計的函式

示例程式碼:

#pragma once
#ifndef __SPECIALIZATION__
#define __SPECIALIZATION__
template<class Key> // 要先有泛化.才有模板的特化
struct hash
{

};

template <>
struct hash<long>
{
size_t operator() (long x) const { return x; }
};

#endif // !__SPECIALIZATION__

partial specialization 模板偏特化(個數的偏特化,範圍的偏特化)

個數偏特化:

template<typename T, typename Alloc=...>
class vector
{

}

T是指定函式型別.Alloc是指定函式的分配器(標準庫的內容)

c++當中最小單位是char型別.八位,如果是bool型別那麼只佔用一位.那麼就不需要使用泛化

實際上就是在宣告模板的時候指定一個模板的泛化值是什麼(類似宣告形參的時候賦予初始值)

示例程式碼:

template<typename Alloc=...>
class vertor<bool, Alloc>
{

}

範圍上的偏特化

如果設計一個模板特化型別T,但是他不是一個具體的型別.他是一個指標.指向任意型別.所以T的範圍就縮小了

示例程式碼:

template<typename T>
class C
{

}

在宣告一個指標的T

template<typename U>
class C<U*>
{

}

如果使用者用的是指標,編譯器會使用下面一套程式碼.如果是指標,那麼編譯器會用上面一套程式碼

T*表示指向什麼都可以

使用者宣告方式:

c<string> obj1; // 使用上面一個T程式碼
c<string*> obj2; // 使用下面一個U*程式碼

template template parameter模板模板引數

宣告一個模板.有兩個引數.第二個引數也是一個模板

示例程式碼:

#pragma once
#ifndef __TEMPLATE__
#define __TEMPLATE__

template<typename T, template<typename T> class Container>
/* 在template尖括號的<>中間.typename和class是共通的 */
class XCls
{
public:

private:
Container<T> c; // 這個T是指前面的typename的T -> 這裡寫出的是list<string>
};

template<typename T>
using Lst = list<T, allocator<T>>

#endif // !__TEMPLATE__

目的是為了讓使用者可以指定使用容器來構造類

容器需要指定元素型別和分配器

上面的模板模板宣告完成後具體使用:

// 錯誤使用方式
// XCls<string, list> mylst1; -> list傳入就是第二個Contrainer,會拿前面的T當前元素型別
// 之所以這裡可以這樣使用,是因為元素有第二模板引數.只是有預設值.如果這樣使用語法過不了
// list的特性接收兩個引數
XCls<string, Lst> mylst;

上訴程式碼當中傳入的list並沒有繫結任何引數,所以它仍然是一個模糊的東西.這樣才可以把它稱之為模板

無法透過的原因是:容器接收好幾個引數

如果只接收一個引數那麼是可以透過的

示例程式碼:

#pragma
#ifndef __TEMPLATETWO__
#define __TEMPLATETWO__

template<typename T, template<typename T> class SmartPtr>
class XCls
{
public:
XCls() : sp(new T) { }
private:
SmartPtr<T> sp;
};

#endif // !__TEMPLATETWO__

使用方式:

XCls<string, shared_ptr> p1;
// XCls<double, unique_ptr> p2; -> 由於unique_ptr的特性所以上述的宣告方式進行這樣構造是不可以的

非模板模板偏特化:

template<class T, class Sequence = deque<T>>
class stack {
friend bool operator== <> (const stack&, const stack&);
friend bool operator< <> (const stack&, const stack&);

protected:
Sequence c; // 底層容器
};

在這段程式碼當中,由於Sequence有預設初始值.所以在宣告呼叫的時候有兩種方式:

stack<int> s1;
stack<int, list<int>> s2;

在這個當中如果要修改第二個初始值.需要指定list<int>並且向list當中傳入引數.那麼它就不是一個模糊的值.而是一個被定義的值.所以不能視為模板

小結

模板是一個模糊的概念,不是一個確定的概念.它只有在使用的時候才會被確定