通过引用c++中的模板函数来传递数组

Passing an array by reference to template function in c++

本文关键字:函数 数组 引用 c++      更新时间:2023-10-16

下面的代码对我来说很好。

#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;
}

我对模板比较陌生,你能解释一下上面的行为吗。

在函数参数中,[](内部没有维度)只是指针的替代语法,因为数组在传递到函数中时会衰减为指针,除非它们是通过引用传递的。

这意味着您的工作通用模板(具有T a[]的模板)与T a*完全相同。如果您在运行时传递大小,那么一切都很好,您可以直接使用它(它将适用于其他未声明为数组的东西,例如std::string::c_str()的返回值)。

然而,如果你想推广tempalte,但仍然将其限制在实际阵列中,你可以这样做:

template<class T, size_t N>
T sum_array(T (&a)[N], int size)
{
    T result=0;
    for(int i=0; i<size; i++)
    {
        result = a[i] + result;
    }
    return result;
}

这样,只有一个真正的数组可以传入,但它的类型T和长度N都将被推导出来。根据您的用例,在这种情况下,您可能会删除size参数。

如果您想通过引用绑定数组,您绝对需要知道数组的大小。不过,可以让编译器推断大小。假设代码中的逻辑是非平凡的,那么最好立即委托给与数组大小无关的版本。这里有一个例子:

template<typename T>
T sum_array(T const* a, int size)
{
    return std::accumulate(a, a + size, T());
}
template <typename T, int Size>
T sum_array(T const (&array)[Size]) {
    return sum_array(array, Size);
}

当然,我忍不住也使用<numeric>中的std::accumulate():如果有算法的话,使用它是个好主意

由于您想知道是否要从数组中删除引用:当对函数参数的类型使用T[]时,它相当于使用T*。即使使用T[10]作为函数参数的类型,编译器也会将其读取为T*

为了提供关于其他所说内容的参考,请参阅模板参数推导:

在扣除开始之前,对p和A的以下调整是制造:

  1. 如果P不是参考类型

a) 如果a是数组类型,则a为替换为从数组到指针获得的指针类型转变

b) 否则,如果A是函数类型,则将A替换为从函数到指针的转换获得的指针类型;

c) 否则,如果A是cv限定类型,则顶级cv限定符被忽略以进行扣除:

    template<class T> void f(T);
    int a[3];
    f(a); // P = T, A = int[3], adjusted to int*: deduced T = int*   
    void b(int);
    f(b); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int)  
    const int c = 13;
    f(c); // P = T, A = const int, adjusted to int: deduced T = int
  1. 如果p是cv合格类型,忽略顶级cv限定符进行推导。

  2. 如果p是引用类型,引用类型用于推导。

  3. 如果p是对cv不合格模板参数的右值引用(所谓转发引用),相应的函数调用参数为对于推导(注:这是std::forward作用的基础注意:在类模板参数推导中类模板从来都不是转发引用(因为C++17)):

基本上,1-a)意味着试图通过值传递数组会触发数组到指针的转换(衰减),从而丢失静态大小信息,而3则表示通过引用传递是保持原始的完整类型及其大小。1-a)

顺便说一句,在非模板上下文中似乎也会发生同样的情况(可能需要有人提供引用:数组到指针的转换)。

这是一个可能的例子:

#include <iostream>
template <size_t N>
constexpr size_t Size(const char [N]) {
    return N;
}
template <size_t N>
constexpr size_t Size2(const char (&)[N]) {
    return N;
}
void Test(const char [5]) {
    std::cout << "Passing array by value" << std::endl;
}
template<typename T>
void Test2(const T [3]) {
    std::cout << "Template passing array by value" << std::endl;
}
void Testr(const char (&)[5]) {
    std::cout << "Passing array by reference" << std::endl;
}
template<typename T>
void Testr2(const T (&)[3]) {
    std::cout << "Template passing array by reference" << std::endl;
}
int main() {
    // pointer to array decay, N cannot be deduced
//    std::cout << Size("Test") << std::endl;
    // reference to "sized" array, N can be deduced
    std::cout << Size2("Test") << std::endl;
    // also pointer to array decay, even in non template context, size provided in signature is not used
    Test("Test");
    Test("TestTest");
    // pointer to array decay, size provided in signature is not used
    Test2("Test");
    Test2("TestTest");
    Testr("Test");
    // reference to "sized" array, size provided in signature is checked
//    Testr("TestTest");
//    Testr2("Test");
    return 0;
}

实时演示