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

问题描述 投票:2回答:4

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

这是我到目前为止:

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参数包?我认为这是省略号的目的。

c++ c++11 average variadic-templates
4个回答
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,然后你可以使用基于范围的循环。


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

}

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{};
}

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.