类型检测:使用可变参数来正确实现一个计算均值的函数

朱迪

我正在尝试了解如何正确实现具有两个初始必需特征的函数以计算数学样本均值:

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当然,我可以用它代替动态数组,但这会使我每次调用该函数时都必须进行一次内存分配和一次内存释放,这是无法接受的浪费。

那么,实现这样的功能来避免上述问题并遵循我的两个初始条件的正确方法是什么?

最高66

要解决“该函数只能使用最少两个参数”的问题,您可以创建一个简单的类型特征以提取第一个类型

template <typename T0, typename ...>
struct firstType
 { using type = T0; };

或者,如果您愿意(并且恕我直言,则更好),请遵循std::common_type_tDiego91b(+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,从而将5ad添加到2total成为7和除以2成为,所以(整数除法)3

在countrary上,如果您呼叫mean(5.5, 2)firstType则类型5.5double因此total成为double中,添加25.5和获得7.5最后一个除法(7.5 / 2)是double给予除法3.75

当您使用调用mean(2, 5.5)(或mean(5.5, 2))时std::common_type_tint之间的常见类型doubledouble; 因此total定义为double并且函数返回7.5 / 23.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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用Python熊猫,如何创建一个函数来计算代表比前一行低的值的行的比例?

在PostgreSQL中,如何声明一个接受不同类型参数的可变参数函数?

使用模板可变参数将多个参数传递给另一个函数

如何编写一个TypeScript函数来接受将接口实现为参数的类?

在foldl的lambda函数中正确实现一个函数

尝试创建一个函数来计算python中数字的总和和平均值

如何编写一个scala函数来接受两种类型的参数?

如何使用可变数目的std :: string_view参数正确实现函数?

可变参数函数无法正确传递第一个参数

用返回类型与其第一个参数相同的函数来定义函数的正确方法是什么

如何创建一个函数来计算年份?

具有可变数量和参数类型的C ++函数作为另一个函数的参数

一个递归函数来计算数字的总和

如何使用“COUNTIF”函数来计算每行的一个实例

如何创建一个函数来计算和返回平均值、最大值和最小值?

我如何正确实现 std::all_of 函数来验证字符串的一部分?

如何使用一个参数来实现函数来交换对象的私有内容

编写一个函数来计算一个带有序列参数和求和的变量

创建一个函数来打印指数而不使用 **

如何使用 JavaFX 正确实现上一个或下一个按钮事件

创建一个函数来计算一个没有内置方法的数字的对数

如何使用构造函数正确实现数据类型的实例 Eq?

创建一个函数来获取另一个函数的参数输入

如何使用两个参数创建一个函数来查找节点是否存在?

Pandas - 编写一个可重用的 Python 函数来聚合和计算加权平均值

创建一个函数来获取 SQL 中的平均值?

创建一个函数来使用基于交易历史的先进先出法计算股票的已实现收益

实现一个函数来计算马尔可夫链的周期变化方差

编写一个函数来计算 r 中的 CAGR