我正在尝试了解如何正确实现具有两个初始必需特征的函数以计算数学样本均值:
1)使用可变参数。
2)不使用两个函数来完成工作,即不使用调用者函数,然后不使用实际执行计算的第二个函数。
3)功能应尽可能通用
我非常清楚已经问过一个非常类似的问题:使用可变参数模板函数计算多个值的平均值。但是,尽管该问题的公认答案似乎已经教会了OP如何做他没有做的一小部分”不知道,它提供的代码实际上是错误的并且无法编译。
因此,我自己的第一次尝试是遵循以下思路:
template <class... Args>
double mean(const Args & ... args)
{
auto total = 0;
for (auto value : { ...args })
{
total += value;
}
return (double)total / sizeof...(args);
}
这里的问题是,在这一行中auto total = 0;
,编译自然无法自动识别对象total
应具有的类型。
然后,我第二次尝试:
template <class T, class... Args>
T mean(const T &t, const Args & ... args)
{
T total = 0;
for (auto value : { args... })
{
total += value;
}
return (T)(total / sizeof...(args));
}
该版本存在以下问题。如果调用方使用混合类型的参数调用该函数,则该方法无效,例如in mean(1, 2.5)
,其中第一个参数自动检测为an int
,第二个参数自动检测为a double
。
我可以通过执行以下操作解决此问题:
template <class T, class... Args>
T mean(const T &t, const Args & ... args)
{
size_t argsSize = sizeof...(args);
T total = t;
T arg_array[] = { args... };
for (size_t i = 0; i< argsSize; i++)
{
total += (T)arg_array[i];
}
return (T)(total / argsSize) ;
}
即使传递的参数具有不同的类型(当然,只要这些类型可以转换为T
),此代码也可以工作。但是,现在的问题是该函数只能使用至少两个参数。如果像in中那样调用它mean(3.14)
,尽管它应该返回3.14,但实际上会引发错误,因为T arg_array[] = { args... }
无法编译,因为无法使用size创建静态数组0
。当然,我可以用它代替动态数组,但这会使我每次调用该函数时都必须进行一次内存分配和一次内存释放,这是无法接受的浪费。
那么,实现这样的功能来避免上述问题并遵循我的两个初始条件的正确方法是什么?
要解决“该函数只能使用最少两个参数”的问题,您可以创建一个简单的类型特征以提取第一个类型
template <typename T0, typename ...>
struct firstType
{ using type = T0; };
或者,如果您愿意(并且恕我直言,则更好),请遵循std::common_type_t
Diego91b(+1)的建议。
您可以mean()
使用C ++ 11编写,如下所示
template <typename ... Args>
typename firstType<Args...>::type mean (Args const & ... args)
// or typename std::common_type<Args...>::type mean (Args const & ... args)
{
using unused = int[];
typename firstType<Args...>::type total { 0 };
// or typename std::common_type<Args...>::type total { 0 };
(void)unused { (total += args, 0)... };
return total / sizeof...(args);
}
在C ++ 14中,可以constexpr
按如下所示进行简化(并转换为)
template <typename ... Args>
constexpr auto mean (Args const & ... args)
{
using unused = int[];
//typename firstType<Args...>::type total { 0 };
std::common_type_t<Args...> total { 0 };
(void)unused { (total += args, 0)... };
return total / sizeof...(args);
}
在C ++ 17中,您可以使用fold表达式(如Passer By所建议),变得非常简单
template <typename ... Args>
constexpr typename firstType<Args...>::type mean (Args const & ... args)
// or constexpr auto mean (Args const & ... args)
{
return (... + args) / sizeof...(Args);
}
以下是完整的可编译C ++ 11示例
#include <iostream>
#include <type_traits>
template <typename T0, typename ...>
struct firstType
{ using type = T0; };
template <typename ... Args>
typename firstType<Args...>::type mean (Args const & ... args)
// or typename std::common_type<Args...>::type mean (Args const & ... args)
{
using unused = int[];
typename firstType<Args...>::type total { 0 };
// or typename std::common_type<Args...>::type total { 0 };
(void)unused { (total += args, 0)... };
return total / sizeof...(args);
}
int main()
{
std::cout << mean(2, 5.5) << std::endl; // print 3 (3.75 with common_type)
std::cout << mean(5.5, 2) << std::endl; // print 3.75
std::cout << mean(2) << std::endl; // print 2
// std::cout << mean() << std::endl; compilation error
}
-编辑-
OP问
您知道为什么在调用mean(2,5.5)时,使用common_type时解决方案给出3.75的原因是什么?
当然。
当你调用mean(2, 5.5)
使用firstType
,第一个参数的类型为类型2
,那就是int
。将Sototal
声明为,int
并将5.5
其添加到总计中,然后将转换为int
,从而将5
ad添加到2
。的total
成为7
和除以2
成为,所以(整数除法)3
。
在countrary上,如果您呼叫mean(5.5, 2)
,firstType
则类型5.5
为double
。因此total
成为double
中,添加2
并5.5
和获得7.5
。最后一个除法(7.5 / 2
)是double
给予的除法3.75
。
当您使用调用mean(2, 5.5)
(或mean(5.5, 2)
)时std::common_type_t
,int
和之间的常见类型double
是double
; 因此total
定义为double
并且函数返回7.5 / 2
为3.75
。
-编辑2-
OP问
是否有特定的原因为什么必须对未使用的使用进行专门分配
int[]
?我尝试将其更改为typename std::common_type<Args...>::type
,在我看来这会使该函数更通用,但随后无法编译。另外,它不适用于double[]
。
该unused
变量是...未使用。接受在初始化时仅声明允许添加total
的值args
,将它们解包。
诀窍是逗号:观察
(total += args, 0)...
逗号将舍弃左侧的值(已计算出该值,因此total
已对其进行了修改,但不适用),然后返回右侧的值。那是零。整数零。由于这个原因int[]
。
最终结果是使用unused
初始化的{ 0, 0, 0, ... }
。
无需将其转换为其他类型的东西。这只是使用args
包装的一个技巧。
如果需要,可以执行此操作(例如)float
;如下
using unused = float[];
(void)unused { (total += args, 0.0f)... };
(观察0.0f
),但是...为什么要这样做?
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句