使用variadic-template函数计算多个值的平均值

时间:2014-07-29 17:06:29

标签: c++ c++11 average variadic-templates

我正在尝试编写一个函数来确定任意数量的参数的平均值,所有这些参数都具有相同的类型。出于学习目的,我试图使用可变参数模板函数来做到这一点。

这是我到目前为止所做的:

template<typename T, class ... Args>
T Mean(Args ... args)
{
    int numArgs = sizeof...(args);
    if (numArgs == 0)
        return T();           // If there are no arguments, just return the default value of that type

    T total;
    for (auto value : args...)
    {
        total += value;
    }

    return total / numArgs;   // Simple arithmetic average (sum divided by total)
}

当我尝试编译它时(使用MS Visual Studio 2013),我收到以下编译错误:

error C3520: 'args' : parameter pack must be expanded in this context (test.cpp)

如何正确“解压缩”args参数包?我认为这是省略号的目的。

4 个答案:

答案 0 :(得分:7)

您可以在参数包扩展周围添加花括号:

template<typename T, class ... Args>
T Mean(Args ... args)
{
    int numArgs = sizeof...(args);
    if (numArgs == 0)
        return T();           // If there are no arguments, just return the default value of that type

    T total;
    for (auto value : {args...})
    {
        total += value;
    }

    return total / numArgs;   // Simple arithmetic average (sum divided by total)
}

这应创建一个std::initializer_list,然后您可以使用基于范围的for循环。

答案 1 :(得分:2)

@Drax的回答可能就是去这里的方法。或者,您可以递归执行此操作,具有自动推导的返回类型,因此您可以混合类型。缺点是你的代码需要更多的编译,所以下面的答案更多的是可变参数模板递归的练习。代码:

#include <iostream>

using namespace std;

template<typename T>
T Mean(T head)
{
    return head;
}

template<typename T, class ... Args>
T Mean(T head, Args... args)
{
    auto N = sizeof...(Args);
    return (head + (N)*Mean(args...)) / (N + 1);  
}

int main(void)
{
    cout << Mean((double)1, (int)2, (float)4) << endl; // (double) 2.3333...
}

或者,使用包装器

#include <iostream>

using namespace std;

template<typename T>
T Mean_wrapper(T head)
{
    return head;
}

// return type is the type of the head of param list
template<typename T, class ... Args>
T Mean_wrapper(T head, Args... args) 
{
    return head + Mean_wrapper(args...);   
}

template<typename T, class ... Args>
T Mean(T head, Args... args)
{
    return Mean_wrapper(head, args...) / (sizeof...(args) + 1);
}

int main(void)
{
    cout << Mean((double)10, (int)20, (float)30) << endl; // (double) 20

}

答案 2 :(得分:1)

请注意,您可以将包扩展为标准容器,并使用常用算法来获得结果。

template <typename T, class... Args, std::size_t N = sizeof...(Args)>
T Mean(Args... args) {
  std::array<T, N> arr = {args...};
  if (N > 0) return std::accumulate(std::begin(arr), std::end(arr), T{}) / N;
  return T{};
}

答案 3 :(得分:1)

递归并考虑参数类型:

#include <iostream>
#include <type_traits>

namespace Detail {

    template <typename T, typename ... Args>
    struct Sum;

    template <typename T>
    struct Sum<T> {
        typedef T type;
        static type apply(T value) { return value; }
    };

    template <typename T, typename ... Args>
    struct Sum {
        typedef decltype(std::declval<T>() + std::declval<typename Sum<Args...>::type>()) type;
        static type apply(T a, Args ...args) {
            return a + Sum<Args...>::apply(args...);
        }
    };
} // namespace Detail

template <typename ... Args>
typename Detail::Sum<Args...>::type sum(Args ... args) {
    return Detail::Sum<Args...>::apply(args...);
}

template <typename ... Args>
typename Detail::Sum<Args...>::type mean(Args ... args) {
    return Detail::Sum<Args...>::apply(args...) / sizeof...(Args);
}


int main()
{
    // 2.5 / 2
    std::cout << mean(int(1), double(1.5)) << '\n';
    return 0;
}