Рассчитайте среднее из нескольких значений с использованием функции вариационного шаблона

StackOverflow https://stackoverflow.com//questions/25021067

Вопрос

Я пытаюсь написать функцию, чтобы определить среднее произвольное количество аргументов, все из которых имеют одинаковый тип.Для целей обучения я пытаюсь сделать это, используя функцию переменных шаблонов.

Это то, что у меня есть так далеко:

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)
}
.

Это должно создать генеракодицетагкод, на котором вы можете использовать диапазон для циклов.

Другие советы

@ Ответ Дракса, наверное, путь сюда.Альтернативно, вы можете сделать это рекурсивно, наличие автоматического выведенного типа возврата, поэтому вы можете смешивать типы.Недостатком является то, что ваш код примет больше для компиляции, поэтому ответ ниже - это больше упражнений в рекурсии вариационных шаблонов.Код:

#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;
}
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top