如何在 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 类型可能是 T
、S
或 decltype(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)
模板只接受一种类型的数据,例如,如果您发送一个双精度数组,那么运行时将推断:
模板 = 双[]
所以每次他看到它时,他都会期待一个双打数组。
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;
我写了这段代码,如果我取消注释最后一行,我会得到错误 - "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 类型可能是
T
、S
或decltype(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)
模板只接受一种类型的数据,例如,如果您发送一个双精度数组,那么运行时将推断:
模板 = 双[]
所以每次他看到它时,他都会期待一个双打数组。
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;