下面的代码对我来说很好。
#include <iostream>
using namespace std;
template<class T>
T sum_array(T (&a)[10], int size)
{
T result=0;
for(int i=0; i<size; i++)
{
result = a[i] + result;
}
return result;
}
int main()
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
cout<<sum_array(a, 10)<<endl;
double d[10] = {1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1};
cout<<sum_array(d, 10)<<endl;
cin.get();
}
但是如果试图通过删除数组大小来使我的函数更通用,如下所示,它会给出一个错误,说没有函数模板的示例。
template<class T>
T sum_array(T (&a)[], int size)
{
T result=0;
for(int i=0; i<size; i++)
{
result = a[i] + result;
}
return result;
}
同时,如果我删除参考,如下所示,它只是工作正常。
template<class T>
T sum_array(T a[], int size)
{
T result=0;
for(int i=0; i<size; i++)
{
result = a[i] + result;
}
return result;
}
我是一个相对较新的模板,请你解释上述行为。
3条答案
按热度按时间sczxawaw1#
在函数参数中,
[]
(内部没有维度)只是指针的替代语法,因为数组在传入函数时会衰减为指针,除非它们通过引用传递。这意味着你的工作通用模板(带有
T a[]
的模板)与T a*
完全相同。如果你在运行时传递大小,一切都很好,你可以使用它(它也适用于其他没有声明为数组的东西,比如std::string::c_str()
的返回值)。但是,如果你想泛化模板,但仍然将其限制在实际数组中,你可以这样做:
这样,只有一个真正的数组可以传入,但它的类型
T
和长度N
都将被推导出来。根据您的用例,在这种情况下,您可能会删除size
参数。qaxu7uf22#
如果你想通过引用绑定一个数组,你绝对需要知道数组的大小。但是,您可以让编译器推断大小。假设代码中的逻辑是重要的,最好立即委托给与数组大小无关的版本。下面是一个例子:
当然,我也忍不住使用
<numeric>
中的std::accumulate()
:如果有一个算法,使用它是一个好主意。既然你想从数组中删除引用:当使用
T[]
作为函数参数的类型时,等效于使用T*
。即使您使用T[10]
作为函数参数的类型,编译器也会将其读取为T*
。c3frrgcw3#
为了给参考关于什么其他说见template argument deduction:
在扣除开始之前,对P和A进行以下调整:
1.如果P不是引用类型,
a)如果A是数组类型,则A被从数组到指针转换获得的指针类型替换;
B)否则,如果A是函数类型,则用从函数到指针转换获得的指针类型替换A;
c)否则,如果A是cv限定类型,则忽略顶级cv限定符进行演绎:
1.如果P是一个cv限定类型,则在演绎时忽略顶级cv限定符。
1.如果P是引用类型,则引用的类型用于演绎。
1.如果P是一个cv限定的模板参数的右值引用(所谓的转发引用),并且对应的函数调用参数是一个左值,则使用对A的类型左值引用代替A进行演绎(注意:这是std::forward操作的基础。在类模板参数推导中,类模板的模板参数永远不是转发引用(C++17起):
基本上,1-a)意味着尝试通过值传递数组会触发数组到指针的转换(衰减),丢失静态大小信息,而3表示通过引用传递保持原始的完整类型及其大小。1-a)
顺便说一下,同样的情况似乎发生在非模板上下文中(需要有人提供引用,也许在那里:Array-to-pointer conversion)。
下面是一个可能的例子:
Live Demo