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。