我正在尝试编写一个函数来确定任意数量的参数的平均值,所有这些参数都具有相同的类型。出于学习目的,我试图使用可变参数模板函数来做到这一点。
这是我到目前为止:
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
参数包?我认为这是省略号的目的。
您可以在参数包扩展周围添加花括号:
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
,然后你可以使用基于范围的循环。
@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
}
请注意,您可以将包扩展为标准容器,并使用常用算法来获得结果。
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{};
}
递归并考虑参数类型:
#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;
}