如何在 C++ 中使用泛型函数?

How to use generic function in c++?

我写了这段代码,如果我取消注释最后一行,我会得到错误 - "template argument deduction/substitution failed: "。是因为 C++ 中泛型函数的某些限制吗?此外,我的程序不会打印数组 b 的浮动答案。有什么我可以做的吗? (很抱歉在单个 post 中问了 2 个问题。)
P.S: 我刚开始学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.0)

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

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)
    

    Return 类型可能是 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)

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)

Live Demo

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

模板 = 双[]

所以每次他看到它时,他都会期待一个双打数组。

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

所以代码

sumArray(b,3,40.0)

会工作

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

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;