如何在 c++ 中使用泛型函数

How to use generic function in c++?

本文关键字:泛型 函数 c++      更新时间:2023-10-16

我写了这段代码,如果我取消注释倒数第二行,我会得到错误 - "模板参数推断/替换失败:"。是因为C++中泛型函数的某些限制吗?此外,我的程序不会为数组b打印浮动答案。我能为此做些什么吗?(很抱歉在一篇文章中问了 2 个问题。
PS:我刚刚开始学习C++。

#include <iostream>
 using namespace std;
 template <class T>
 T sumArray(  T arr[], int size, T s =0)
 {
     int i;
     for(i=0;i<size;i++)
     {  s += arr[i];
     }
     return s;
 }
 int main()
 {
     int a[] = {1,2,3};
     double b[] = {1.0,2.0,3.0};
     cout << sumArray(a,3) << endl;
     cout << sumArray(b,3) << endl;
     cout << sumArray(a,3,10) << endl;
     //cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
     return 0;
 }


编辑 1:将 40 更改为 40.0 后,代码有效。这是我得到的输出:
6
6
16
46
在第二种情况下,我仍然没有得到浮动答案。有什么建议吗?

原因是编译器无法推断出T的类型。

它应该如何理解您的最后一个示例的T是什么?第一个参数(b(的类型是double[],而在函数定义中是T[]的。因此,看起来T应该是double.但是,第三个参数(40(的类型是int的,所以看起来T应该int。因此错误。

40更改为40.0使其正常工作。另一种方法是在模板声明中使用两种不同的类型:

#include <iostream>
 using namespace std;
 template <class T, class S = T>
 T sumArray(  T arr[], int size, S s =0)
 {
     int i;
     T res = s;
     for(i=0;i<size;i++)
     {  res += arr[i];
     }
     return res;
 }
 int main()
 {
     int a[] = {1,2,3};
     double b[] = {1.0,2.0,3.1};
     cout << sumArray(a,3) << endl;
     cout << sumArray(b,3) << endl;
     cout << sumArray(a,3,10) << endl;
     cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
     return 0;
 }

请注意,我必须显式地将s转换为T,否则最后一个示例将丢失小数部分。

但是,此解决方案仍然不适用于sumArray(a,3,10.1),因为它会10.1投射到int ,因此如果这也是一个可能的用例,则需要更准确的处理。使用 c++11 功能的完整工作示例可能如下所示

 template <class T, class S = T>
 auto sumArray(T arr[], int size, S s=0) -> decltype(s+arr[0])
 {
    int i;
    decltype(s+arr[0]) res = s;
    ...

此模板函数的另一个可能的改进是自动扣除数组大小,请参阅TartanLlama的答案。

sumArray(b,3,40)

40的类型是int,但b的类型是double[3]。当您将这些作为参数传入时,编译器会获得T冲突的类型。

解决此问题的一种简单方法是传入double

sumArray(b,3,40.0)

但是,最好通过添加另一个模板参数来允许在调用站点进行转换。您还可以添加一个来推断数组的大小,这样您就不需要显式传递它:

template <class T, class U=T, std::size_t size>
U sumArray(T (&arr) [size], U s = 0)

U参数默认为 T 以支持 s 的默认值。请注意,要推断数组的大小,我们需要传递对它的引用,而不是按值传递,这将导致它衰减到指针。

调用现在如下所示:

sumArray(b,40)

现场演示

in

template <class T>
T sumArray(  T arr[], int size, T s =0)
             ^                  ^

两者(可推导的(T应该匹配。

sumArray(b, 3, 40)中,第一个是double,第二个是int

有几种方法可以解决问题

  • 在呼叫站点,呼叫sumArray(b, 3, 40.0)sumArray<double>(b, 3, 40);

  • 使用额外参数:

    template <typename T, typename S>
    auto sumArray(T arr[], int size, S s = 0)
    

    返回类型可以是 TSdecltype(arr[0] + s),具体取决于您的需求。

  • 使参数不可推导:

    template <typename T> struct identity { using type = T;};
    // or template<typename T> using identity = std::enable_if<true, T>;
    template <typename T>
    T sumArray(T arr[], int size, typename identity<T>::type s = 0)
    

应该是

sumArray(b,3,40.0)

所以,T将被推导出为double.在您的代码中,它是int.

当推导失败时,另一种选择是明确地告诉编译器你的意思:

cout << sumArray<double>(b,3,40) << endl;
编译器不知道

T应该int还是double

您可能需要执行以下操作,以保留传递类型的最高精度:

template <class T, class S>
std::common_type_t <T, S> sumArray (T arr [], std::size_t size, S s = 0)
{
   std::common_type_t <T, S> sum = s;
   for (std::size_t i = 0; i != size; ++i)
   {
      sum += arr[i];
   }
   return sum;
}

但是,您正在编写的函数已经存在。它std::accumulate

std::cout << std::accumulate (std::begin (b), std::end (b), 0.0) << std::endl;

模板只接受一种类型的数据,例如,如果您发送一个双精度数组,则运行时将推断

模板 = 双[]

所以每次他看到它时,他都会期待一系列的双打。

sumArray(b,3,40)传递"b"(这是一个双精度数组(,但随后传递运行时无法隐式转换为双精度的"40"。

所以代码

sumArray(b,3,40.0)

将工作