问题
I am trying to write a function to determine the average of an arbitrary number of arguments, all of which have the same type. For learning purposes I am trying to do this using a variadic-templated function.
This is what I have so far:
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)
}
When I try to compile this (using MS Visual Studio 2013) I get the following compilation error:
error C3520: 'args' : parameter pack must be expanded in this context (test.cpp)
How should I properly "unpack" the args
parameter pack? I thought that was the purpose of the ellipses.
回答1:
You can add curly braces around your parameter pack expansion:
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)
}
This should create an std::initializer_list
on which you can then use range-based for loops.
回答2:
@Drax's answer is probably the way to go here. Alternatively, you can do it recursively, having the return type auto-deduced, so you can mix types. The disadvantage is that your code will take more to compile, so the answer below is more of an exercise in variadic templates recursion. Code:
#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...
}
or, with a wrapper,
#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
}
回答3:
Note that you can expand the pack into a standard container and use the usual algorithms to get the result.
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{};
}
回答4:
Recursively and considering argument types:
#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;
}
来源:https://stackoverflow.com/questions/25021067/calculate-the-average-of-several-values-using-a-variadic-template-function