C++ 的模板機制不僅允許您使用型別(如
std::vector<int>
中的
int
)進行引數化,還可以使用值進行引數化。非型別模板引數可以是以下型別
[1]
- 整型(或列舉)值
- 物件/函式指標
- 物件/函式引用
- 成員指標
我將探討其中第一種型別——整數——以及模板引數推導如何處理陣列。
模板引數推導是指在模板引數未指定時,編譯器如何確定例項化模板的機制,例如:
1 2
|
std::vector<int> vi;
std::sort(vi.begin(), vi.end());
|
儘管我們沒有為
std::sort()
指定要使用的迭代器型別,但編譯器會根據我們提供的引數進行推導。
陣列維度作為模板引數
我們可以建立一個以陣列維度為模板的函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#include <iostream>
#include <string>
template<int N>
void fun(std::string s[N])
{
for (int i(0); i < N; ++i)
std::cout << i << ": " << s[i] << std::endl;
}
int main()
{
std::string s[2] = {"hello", "world"};
fun<2>(s);
}
|
$> ./a.out
0: hello
1: world
|
請注意,在此實現中省略顯式模板引數,改用
fun(s)
呼叫,將會導致編譯錯誤。
$> g++ broken.cpp
broken.cpp: In function ‘int main()’:
broken.cpp:14:9: error: no matching function for call to ‘fun(std::string [2])’ |
這讓我困惑了一段時間,因為我一直認為模板引數可以從陣列維度推匯出來。
(注:順便說一句,如果您寫成 fun<500>(s)
也能正常工作;我認為這是因為陣列會退化為指標,然後指標可以輕鬆地初始化陣列引數。)
從陣列維度推導模板引數
Stroustrup 的 TCPL 說道
[2],“
編譯器可以從型別為...type[I]
的函式模板引數中推匯出非型別模板引數 I
”,這對我來說意味著上述情況應該能夠正常工作。
我花了些時間琢磨為什麼引數不能被推匯出來,最終找到了答案。標準規定,型別為“
N T
的陣列”(例如,“5 個
int
的陣列”)的值可以轉換為型別為“
T
的指標”的右值。
[3] 這意味著陣列的大小在例項化過程中丟失了,因此
N
的值無法推導,模板例項化失敗,並且——在我們上面的例子中——無法解析
fun()
。
防止這種轉換(稱為“退化”)的方法是將函式引數宣告為陣列的
引用,方法是將
fun(string s[N])
改為
fun(string (&s)[N])
。
1 2 3 4 5 6 7 8 9 10 11 12
|
template<int N>
void fun(string (&s)[N])
{
for (int i(0); i < N; ++i)
cout << i << ": " << s[i] << endl;
}
int main()
{
string s[2] = {"hello", "world"};
fun(s);
}
|
這樣就能正常工作了!
多維陣列
有趣的是,雖然在這個多維陣列的替代實現中我沒有宣告陣列的引用,但它仍然正常工作。
1 2 3 4 5 6 7 8 9 10 11 12
|
template<int N>
void fun(string s[1][N])
{
for (int i(0); i < N; ++i)
cout << i << ": " << s[0][i] << endl;
}
int main()
{
string s[1][2] = {{"hello", "world"}};
fun(s);
}
|
原因是陣列退化不會遞迴發生。因此,在呼叫
fun()
時,
int[1][2]
退化為指向 2 個 int 陣列的指標,僅此而已,之後不再退化,因此仍然保留了大小資訊。(注:我找不到這方面的權威證據;這可能是在標準中沒有說明它應該遞迴發生的情況下隱含的。)
本文最初發表於
The other branch。
腳註
- 這是 C++98 和 03 中指定的列表(參見 ISO C++ 標準 14882 14.1.4);C++11 增加了一些內容。
- Stroustrup - The C++ Programming Language, Special Edition;附錄 C.13.4 - Deducing Function Template Arguments
- ISO C++ 標準 14882 4.2.1。