在C++中通过引用向模板函数传递数组

bd1hkmkf  于 2023-05-20  发布在  其他
关注(0)|答案(3)|浏览(102)

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

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

我是一个相对较新的模板,请你解释上述行为。

sczxawaw

sczxawaw1#

在函数参数中,[](内部没有维度)只是指针的替代语法,因为数组在传入函数时会衰减为指针,除非它们通过引用传递。
这意味着你的工作通用模板(带有T a[]的模板)与T a*完全相同。如果你在运行时传递大小,一切都很好,你可以使用它(它也适用于其他没有声明为数组的东西,比如std::string::c_str()的返回值)。
但是,如果你想泛化模板,但仍然将其限制在实际数组中,你可以这样做:

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参数。

qaxu7uf2

qaxu7uf22#

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

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*

c3frrgcw

c3frrgcw3#

为了给参考关于什么其他说见template argument deduction
在扣除开始之前,对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限定符。
1.如果P是引用类型,则引用的类型用于演绎。
1.如果P是一个cv限定的模板参数的右值引用(所谓的转发引用),并且对应的函数调用参数是一个左值,则使用对A的类型左值引用代替A进行演绎(注意:这是std::forward操作的基础。在类模板参数推导中,类模板的模板参数永远不是转发引用(C++17起):
基本上,1-a)意味着尝试通过值传递数组会触发数组到指针的转换(衰减),丢失静态大小信息,而3表示通过引用传递保持原始的完整类型及其大小。1-a)
顺便说一下,同样的情况似乎发生在非模板上下文中(需要有人提供引用,也许在那里:Array-to-pointer conversion)。
下面是一个可能的例子:

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

Live Demo

相关问题