首先咱們定義一下本文通用的模板定義與調用:c++
template<typename T> void f(ParamType param); ...... f(expr); // call f with some expression
在編譯階段使用expr
來推斷ParamType
和T
這兩個類型。這兩個類型一般不一樣,由於ParamType
會有const
和引用等修飾。例如:express
template<typename T> void f(const T& param); // ParamType is const T&
int x = 0; f(x); // call f with an int
這裏,T被推斷成int
,可是ParamType
的類型是const T&
。數組
直覺下T
的類型應該和expr
的同樣,好比上面的例子中,expr
和T
的類型都是int
。可是會有一些例外狀況:T
的類型不只依賴expr
,還依賴ParamType
。總共分爲三大類:app
ParamType
是一個指針或者引用,但不是universal reference
(或者叫forwarding references
).ParamType
是一個universal reference
。ParamType
既不是指針也不是引用。expr
是一個引用,忽略其引用部分。expr
與ParamType
的類型來決定T
的類型。template<typename T> void f(T& param); // param is a reference ...... int x = 27; // x is an int const int cx = x; // cx is a const int const int& rx = x; // rx is a reference to x as a const int // call f f(x); // T is int, param's type is int& f(cx); // T is const int, param's type is const int& f(rx); // T is const int, param's type is const int&
上面例子是左值引用,可是這點對右值引用也適用。
注意第三點,const
修飾符依舊保留。 這和普通函數的相似調用有區別:函數
void f(int &x){ } ... const int x = 10; f(x); // error
若是給ParamType
加上const
,狀況也沒有太大變化:指針
template<typename T> void f(const T& param); // param is now a ref-to-const ...... int x = 27; // as before const int cx = x; // as before const int& rx = x; // as before ...... f(x); // T is int, param's type is const int& f(cx); // T is int, param's type is const int& f(rx); // T is int, param's type is const int&
改成指針也同樣:code
template<typename T> void f(T* param); // param is now a pointer ...... int x = 27; const int *px = &x; f(&x); // T is int, param's type is int* f(px); // T is const int, param's type is const int*
expr
是左值,那麼T
和ParamType
會被推斷爲左值引用。expr
是右值,那麼就是Case 1的狀況。template<typename T> void f(T&& param); // param is now a universal reference ...... int x = 27; const int cx = x; const int& rx = x;
調用:對象
f(x); // x is lvalue, so T is int&, param's type is also int& f(cx); // cx is lvalue, so T is const int&, param's type is also const int& f(rx); // rx is lvalue, so T is const int&, param's type is also const int& f(27); // 27 is rvalue, so T is int, param's type is therefore int&&
若是以前瞭解過完美轉發和摺疊引用的概念,結合Case1,這一個規則仍是比較好理解的。it
這兩點須要區分清楚,好比:io
template<typename T> void f(T&& param); // universal reference template<typename T> void f(std::vector<T>&& param); // rvalue reference
有一個通用規則 : universal reference
會有類型推斷的過程。具體在後面的單獨文章會講,跟這篇文章的主題關係不大,這裏稍微提一下 : )
這種狀況就是pass-by-value的狀況:
template<typename T> void f(T param); // param is now passed by value
這意味着,param是一個被拷貝的全新對象,也就是param決定着T的類型:
expr
是引用類型,忽略。expr
帶有const、volatile,忽略。int x = 27; const int cx = x; const int& rx = x; f(x); // T's and param's types are both int f(cx); // T's and param's types are again both int f(rx); // T's and param's types are still both int
忽略const和volatile也比較好理解:參數是值拷貝,因此形參和實參實際上是互相獨立的。正以下面代碼能夠將const int
傳遞給int
,可是聲明爲引用則不行:
void f(int x){ } int main() { const int x = 10; f(x); }
注意忽略的const是針對參數自己的,而不針對指針指向的const對象:
template<typename T> void f(T param); ...... const char* const ptr = "Fun with pointers"; // ptr is const pointer to const object f(ptr); // pass arg of type const char * const
這個按照值傳遞的是ptr,因此ptr的const會被忽略,可是ptr指向的對象依然是const。
數組類型和指針類型是兩種類型,可是有時候他們是能夠互換的,好比在下面這種狀況下,數組會decay成指針:
const char name[] = "J. P. Briggs"; // name's type is const char[13] const char * ptrToName = name; // array decays to pointer
在普通函數中,函數形參爲數組類型和指針類型是等價的:
void myFunc(int param[]); void myFunc1(int* param); // same function as above
可是數組做爲模板參數是比較特殊的一種狀況。
template<typename T> void f(T param); // template with by-value parameter ...... const char name[] = "J. P. Briggs"; // name's type is const char[13] f(name); // name is array, but T deduced as const char*
這種狀況下,T
被推斷爲指針類型const char*
.
template<typename T> void f(T& param); ...... const char name[] = "J. P. Briggs"; // name's type is const char[13] f(name); // pass array to f
如今T
被推斷爲數組類型const char [13]
,ParamType
爲const char (&)[13]
,這種狀況是很特殊的,要與ParamType
按值傳遞區分開。
咱們能夠利用上面這種特性定義一個模板來推斷數組的大小,這種用法還蠻常見的:
template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&)[N]) noexcept { return N; } ...... int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; std::array<int, arraySize(keyVals)> mappedVals;
上面討論的關於數組的狀況一樣適用於函數做爲參數,函數類型一樣也能夠decay
成函數指針:
void someFunc(int, double); // someFunc is a function;type is void(int, double) template <typename T> void f1(T param); // in f1, param passed by value template <typename T> void f2(T ¶m); // in f2, param passed by ref f1(someFunc); // param deduced as ptr-to-func; type is void (*)(int, double) f2(someFunc); // param deduced as ref-to-func; type is void (&)(int, double)
不過這在平時應用中也沒有太大差異。
(完)
朋友們能夠關注下個人公衆號,得到最及時的更新: