конечный тип возвращаемого значения с использованием decltype с функцией шаблона с переменным числом аргументов

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

Вопрос

Я хочу написать простой сумматор (для смеха), который складывает каждый аргумент и возвращает сумму соответствующего типа.На данный момент у меня есть это:

#include <iostream>
using namespace std;

template <class T>
T sum(const T& in)
{
   return in;
}

template <class T, class... P>
auto sum(const T& t, const P&... p) -> decltype(t + sum(p...))
{
   return t + sum(p...);
}

int main()
{
   cout << sum(5, 10.0, 22.2) << endl;
}

В GCC 4.5.1 это, кажется, работает нормально для двух аргументов, например.sum(2, 5.5) возвращает значение 7.5.Однако при большем количестве аргументов я получаю ошибки о том, что sum() просто еще не определен.Однако если я объявлю sum() следующим образом:

template <class T, class P...>
T sum(const T& t, const P&... p);

Тогда это работает для любого количества аргументов, но sum(2, 5.5) вернет целое число 7, чего я не ожидал.Я предполагаю, что при наличии более двух аргументов decltype() придется выполнить какую-то рекурсию, чтобы определить тип t + sum(p...).Это законный C++0x?или decltype() работает только с невариативными объявлениями?Если это так, то как бы вы написали такую ​​функцию?

Это было полезно?

Решение

Я думаю, проблема в том, что шаблон вариативной функции считается только объявленным. после вы указали тип возвращаемого значения, чтобы sum в decltype никогда не может ссылаться на сам шаблон вариативной функции.Но я не уверен, является ли это ошибкой GCC или C++0x просто не позволяет этого.Мой предполагать заключается в том, что C++0x не допускает "рекурсивный" вызов в ->decltype(expr) часть.

В качестве обходного пути мы можем избежать этого «рекурсивного» вызова в ->decltype(expr) с пользовательским классом черт:

#include <iostream>
#include <type_traits>
using namespace std;

template<class T> typename std::add_rvalue_reference<T>::type val();

template<class T> struct id{typedef T type;};

template<class T, class... P> struct sum_type;
template<class T> struct sum_type<T> : id<T> {};
template<class T, class U, class... P> struct sum_type<T,U,P...>
: sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};

Таким образом, мы можем заменить decltype в вашей программе с typename sum_type<T,P...>::type и он скомпилируется.

Редактировать: Поскольку это действительно возвращает decltype((a+b)+c) вместо decltype(a+(b+c)) что было бы ближе к тому, как вы используете сложение, вы можете заменить последнюю специализацию следующим:

template<class T, class U, class... P> struct sum_type<T,U,P...>
: id<decltype(
      val<T>()
    + val<typename sum_type<U,P...>::type>()
)>{};

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

Решение С++ 14:

template <class T, class... P>
auto sum(const T& t, const P&... p){
    return t + sum(p...);
}

Тип возврата вычитается автоматически.

Посмотрите это в онлайн-компиляторе

Судя по всему, вы не можете использовать decltype рекурсивно (по крайней мере, на данный момент, возможно, они это исправят)

Вы можете использовать структуру шаблона, чтобы определить тип суммы.

Выглядит некрасиво, но работает

#include <iostream>
using namespace std;


template<typename... T>
struct TypeOfSum;

template<typename T>
struct TypeOfSum<T> {
    typedef T       type;
};

template<typename T, typename... P>
struct TypeOfSum<T,P...> {
    typedef decltype(T() + typename TypeOfSum<P...>::type())        type;
};



template <class T>
T sum(const T& in)
{
   return in;
}

template <class T, class... P>
typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p)
{
   return t + sum(p...);
}

int main()
{
   cout << sum(5, 10.0, 22.2) << endl;
}

Еще один ответ на последний вопрос с меньшим количеством ввода текста с использованием C++ 11. std::common_type:Просто используйте

std::common_type<T, P ...>::type

в качестве возвращаемого типа вашей переменной суммы.

Касательно std::common_type, вот отрывок из http://en.cppreference.com/w/cpp/types/common_type:

Для арифметических типов общий тип также можно рассматривать как тип арифметического выражения (возможно, смешанного), такого как T0() + T1() + ...+ Тн().

Но очевидно, что это работает только для арифметических выражений и не решает общей проблемы.

Я предоставляю это улучшение принятого ответа.Всего две структуры

#include <utility>

template <typename P, typename... Ps>
struct sum_type {
    using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>());
};

template <typename P>
struct sum_type<P> {
    using type = P;
};

Теперь просто объявите свои функции как

template <class T>
auto sum(const T& in) -> T
{
   return in;
}

template <class P, class ...Ps>
auto sum(const P& t, const Ps&... ps) -> typename sum_type<P, Ps...>::type
{
   return t + sum(ps...);
}

Благодаря этому ваш тестовый код теперь работает.

std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl;

146.5

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